From d7279e4c883aabec5b4549ffd9e72803a6411030 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 02:51:23 +0100 Subject: [PATCH 01/39] Support for Camera Entities. - New entity type: camera, with just the basic properties of an entity (position, rotation, name etc.). - New CameraMode: CameraEntity - Example script using the new cameraEntity - Updated edit.js to use the CameraEntities (with button to preview the camera) --- examples/edit.js | 23 ++++++++++++++++ interface/src/Application.cpp | 21 +++++++++++++-- interface/src/Camera.cpp | 27 +++++++++++++++++++ interface/src/Camera.h | 8 ++++++ interface/src/Menu.cpp | 3 +++ interface/src/Menu.h | 1 + libraries/entities/src/CameraEntityItem.cpp | 30 +++++++++++++++++++++ libraries/entities/src/CameraEntityItem.h | 27 +++++++++++++++++++ libraries/entities/src/EntityTypes.cpp | 4 ++- libraries/entities/src/EntityTypes.h | 3 ++- 10 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 libraries/entities/src/CameraEntityItem.cpp create mode 100644 libraries/entities/src/CameraEntityItem.h diff --git a/examples/edit.js b/examples/edit.js index 7a16030afc..eccb9a152c 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -155,6 +155,7 @@ var toolBar = (function() { newWebButton, newZoneButton, newPolyVoxButton, + newCameraButton, browseMarketplaceButton; function initialize() { @@ -299,6 +300,20 @@ var toolBar = (function() { visible: false }); + newCameraButton = toolBar.addTool({ + imageURL: toolIconUrl + "polyvox.svg", + subImage: { + x: 0, + y: 0, + width: 256, + height: 256 + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + that.setActive(false); } @@ -345,6 +360,7 @@ var toolBar = (function() { toolBar.showTool(newWebButton, doShow); toolBar.showTool(newZoneButton, doShow); toolBar.showTool(newPolyVoxButton, doShow); + toolBar.showTool(newCameraButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -601,6 +617,13 @@ var toolBar = (function() { } + return true; + } + if (newCameraButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Camera" + }); + return true; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d41679bea..f34462f2eb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1187,6 +1187,19 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + } else if (_myCamera.getMode() == CAMERA_MODE_CAMERA_ENTITY) { + EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); + if (cameraEntity != nullptr) { + if (isHMDMode()) { + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(cameraEntity->getPosition() + hmdOffset); + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + _myCamera.setRotation(cameraEntity->getRotation() * hmdRotation); + } else { + _myCamera.setPosition(cameraEntity->getPosition()); + _myCamera.setRotation(cameraEntity->getRotation()); + } + } } // Update camera position if (!isHMDMode()) { @@ -2640,8 +2653,8 @@ void Application::cycleCamera() { menu->setIsOptionChecked(MenuOption::ThirdPerson, false); menu->setIsOptionChecked(MenuOption::FullscreenMirror, true); - } else if (menu->isOptionChecked(MenuOption::IndependentMode)) { - // do nothing if in independe mode + } else if (menu->isOptionChecked(MenuOption::IndependentMode) || menu->isOptionChecked(MenuOption::CameraEntityMode)) { + // do nothing if in independent or camera entity modes return; } cameraMenuChanged(); // handle the menu change @@ -2668,6 +2681,10 @@ void Application::cameraMenuChanged() { if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { _myCamera.setMode(CAMERA_MODE_INDEPENDENT); } + } else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) { + if (_myCamera.getMode() != CAMERA_MODE_CAMERA_ENTITY) { + _myCamera.setMode(CAMERA_MODE_CAMERA_ENTITY); + } } } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 17c745bdba..ae654c4906 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -28,6 +28,8 @@ CameraMode stringToMode(const QString& mode) { return CAMERA_MODE_MIRROR; } else if (mode == "independent") { return CAMERA_MODE_INDEPENDENT; + } else if (mode == "camera entity") { + return CAMERA_MODE_CAMERA_ENTITY; } return CAMERA_MODE_NULL; } @@ -41,6 +43,8 @@ QString modeToString(CameraMode mode) { return "mirror"; } else if (mode == CAMERA_MODE_INDEPENDENT) { return "independent"; + } else if (mode == CAMERA_MODE_CAMERA_ENTITY) { + return "camera entity"; } return "unknown"; } @@ -94,6 +98,26 @@ void Camera::setMode(CameraMode mode) { emit modeUpdated(modeToString(mode)); } +QUuid Camera::getCameraEntity() const { + if (_cameraEntity != nullptr) { + return _cameraEntity->getID(); + } + return QUuid(); +}; + +void Camera::setCameraEntity(QUuid cameraEntityID) { + EntityItemPointer entity = qApp->getEntities()->getTree()->findEntityByID(cameraEntityID); + if (entity == nullptr) { + qDebug() << "entity pointer not found"; + return; + } + if (entity->getType() != EntityTypes::Camera) { + qDebug() << "entity type is not camera"; + return; + } + _cameraEntity = entity; +} + void Camera::setProjection(const glm::mat4& projection) { _projection = projection; } @@ -118,6 +142,9 @@ void Camera::setModeString(const QString& mode) { case CAMERA_MODE_INDEPENDENT: Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); break; + case CAMERA_MODE_CAMERA_ENTITY: + Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); + break; default: break; } diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 7f7515cf5f..7aa31456e9 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -24,6 +24,7 @@ enum CameraMode CAMERA_MODE_FIRST_PERSON, CAMERA_MODE_MIRROR, CAMERA_MODE_INDEPENDENT, + CAMERA_MODE_CAMERA_ENTITY, NUM_CAMERA_MODES }; @@ -36,6 +37,7 @@ class Camera : public QObject { Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString) + Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) public: Camera(); @@ -49,6 +51,8 @@ public: void loadViewFrustum(ViewFrustum& frustum) const; ViewFrustum toViewFrustum() const; + EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; } + public slots: QString getModeString() const; void setModeString(const QString& mode); @@ -68,6 +72,9 @@ public slots: const glm::mat4& getProjection() const { return _projection; } void setProjection(const glm::mat4& projection); + QUuid getCameraEntity() const; + void setCameraEntity(QUuid cameraEntityID); + PickRay computePickRay(float x, float y); // These only work on independent cameras @@ -97,6 +104,7 @@ private: glm::quat _rotation; bool _isKeepLookingAt{ false }; glm::vec3 _lookingAt; + EntityItemPointer _cameraEntity; }; #endif // hifi_Camera_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1565db2905..88a33c6f34 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -279,6 +279,9 @@ Menu::Menu() { cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu, MenuOption::IndependentMode, 0, false, qApp, SLOT(cameraMenuChanged()))); + cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu, + MenuOption::CameraEntityMode, 0, + false, qApp, SLOT(cameraMenuChanged()))); cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu, MenuOption::FullscreenMirror, 0, // QML Qt::Key_H, false, qApp, SLOT(cameraMenuChanged()))); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 162fad1b9f..5c84bb11fa 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -157,6 +157,7 @@ namespace MenuOption { const QString Bookmarks = "Bookmarks"; const QString CachesSize = "RAM Caches Size"; const QString CalibrateCamera = "Calibrate Camera"; + const QString CameraEntityMode = "Camera Entity"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; const QString Collisions = "Collisions"; diff --git a/libraries/entities/src/CameraEntityItem.cpp b/libraries/entities/src/CameraEntityItem.cpp new file mode 100644 index 0000000000..2c6ca903b3 --- /dev/null +++ b/libraries/entities/src/CameraEntityItem.cpp @@ -0,0 +1,30 @@ +// +// CameraEntityItem.cpp +// libraries/entities/src +// +// Created by Thijs Wenker on 11/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "EntitiesLogging.h" +#include "EntityItemID.h" +#include "EntityItemProperties.h" +#include "CameraEntityItem.h" + + +EntityItemPointer CameraEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItemPointer result { new CameraEntityItem(entityID, properties) }; + return result; +} + +// our non-pure virtual subclass for now... +CameraEntityItem::CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + EntityItem(entityItemID) +{ + _type = EntityTypes::Camera; + + setProperties(properties); +} diff --git a/libraries/entities/src/CameraEntityItem.h b/libraries/entities/src/CameraEntityItem.h new file mode 100644 index 0000000000..3b046ddc90 --- /dev/null +++ b/libraries/entities/src/CameraEntityItem.h @@ -0,0 +1,27 @@ +// +// CameraEntityItem.h +// libraries/entities/src +// +// Created by Thijs Wenker on 11/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_CameraEntityItem_h +#define hifi_CameraEntityItem_h + +#include "EntityItem.h" + +class CameraEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); + + ALLOW_INSTANTIATION // This class can be instantiated + +}; + +#endif // hifi_CameraEntityItem_h diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 52c2242629..b7d9077f57 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,6 +29,7 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" +#include "CameraEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -48,7 +49,8 @@ REGISTER_ENTITY_TYPE(ParticleEffect) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) -REGISTER_ENTITY_TYPE(PolyLine); +REGISTER_ENTITY_TYPE(PolyLine) +REGISTER_ENTITY_TYPE(Camera); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 30b6edbc07..82bbffe254 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,7 +48,8 @@ public: Line, PolyVox, PolyLine, - LAST = PolyLine + Camera, + LAST = Camera } EntityType; static const QString& getEntityTypeName(EntityType entityType); From d60661ff8a75bf5d10029778cc53c8de8bf0f464 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 03:24:32 +0100 Subject: [PATCH 02/39] preview camera button in edit.js script --- examples/edit.js | 13 +++++++++---- examples/html/entityProperties.html | 11 +++++++++-- examples/libraries/entityPropertyDialogBox.js | 13 ++++++++++++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index eccb9a152c..ff4021c46f 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -301,12 +301,12 @@ var toolBar = (function() { }); newCameraButton = toolBar.addTool({ - imageURL: toolIconUrl + "polyvox.svg", + imageURL: toolIconUrl + "light.svg", subImage: { x: 0, - y: 0, - width: 256, - height: 256 + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, @@ -1640,6 +1640,11 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } + } else if (data.action == "previewCamera") { + if (selectionManager.hasSelection()) { + Camera.mode = "camera entity"; + Camera.cameraEntity = selectionManager.selections[0]; + } } else if (data.action == "rescaleDimensions") { var multiplier = data.percentage / 100; if (selectionManager.hasSelection()) { diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 412b413b2b..bc6a6920d1 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -382,7 +382,7 @@ var elHyperlinkHref = document.getElementById("property-hyperlink-href"); var elHyperlinkDescription = document.getElementById("property-hyperlink-description"); - + var elPreviewCameraButton = document.getElementById("preview-camera-button"); if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { @@ -931,6 +931,12 @@ action: "centerAtmosphereToZone", })); }); + elPreviewCameraButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "previewCamera" + })); + }); window.onblur = function() { // Fake a change event @@ -1032,7 +1038,7 @@
- +
@@ -1044,6 +1050,7 @@
+
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index 4fd24756e0..b47e26579d 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -204,7 +204,7 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Collisions Will Move:", type: "checkbox", value: properties.collisionsWillMove }); index++; array.push({ label: "Collision Sound URL:", value: properties.collisionSoundURL }); - index++; + index++; array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) }); index++; @@ -260,6 +260,12 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Cutoff (in degrees):", value: properties.cutoff }); index++; } + + if (properties.type == "Camera") { + array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" }); + index++; + } + array.push({ button: "Cancel" }); index++; @@ -268,6 +274,11 @@ EntityPropertyDialogBox = (function () { }; Window.inlineButtonClicked.connect(function (name) { + if (name == "previewCamera") { + Camera.mode = "camera entity"; + Camera.cameraEntity = propertiesForEditedEntity.id; + } + if (name == "resetDimensions") { Window.reloadNonBlockingForm([ { value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX }, From 184f793cdd2a17f63257170958802ab5d7d9e7b0 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 19:15:29 +0100 Subject: [PATCH 03/39] Camera properties were not stored --- libraries/entities/src/CameraEntityItem.cpp | 54 +++++++++++++++++++ libraries/entities/src/CameraEntityItem.h | 20 +++++++ libraries/entities/src/EntityItemProperties.h | 1 + 3 files changed, 75 insertions(+) diff --git a/libraries/entities/src/CameraEntityItem.cpp b/libraries/entities/src/CameraEntityItem.cpp index 2c6ca903b3..8370447389 100644 --- a/libraries/entities/src/CameraEntityItem.cpp +++ b/libraries/entities/src/CameraEntityItem.cpp @@ -28,3 +28,57 @@ CameraEntityItem::CameraEntityItem(const EntityItemID& entityItemID, const Entit setProperties(properties); } + + +EntityItemProperties CameraEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class + return properties; +} + +bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "CameraEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties._lastEdited); + } + + return somethingChanged; +} + +int CameraEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + return bytesRead; +} + + +// TODO: eventually only include properties changed since the params.lastViewFrustumSent time +EntityPropertyFlags CameraEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + return requestedProperties; +} + +void CameraEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; +} + diff --git a/libraries/entities/src/CameraEntityItem.h b/libraries/entities/src/CameraEntityItem.h index 3b046ddc90..6256c200c2 100644 --- a/libraries/entities/src/CameraEntityItem.h +++ b/libraries/entities/src/CameraEntityItem.h @@ -22,6 +22,26 @@ public: ALLOW_INSTANTIATION // This class can be instantiated + // methods for getting/setting all properties of an entity + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; + virtual bool setProperties(const EntityItemProperties& properties); + + // TODO: eventually only include properties changed since the params.lastViewFrustumSent time + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged); + }; #endif // hifi_CameraEntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b95f4d35f4..4522e4fc45 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -64,6 +64,7 @@ class EntityItemProperties { friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods + friend class CameraEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties() = default; From 128a0c2397eeae266ce068023d1271f6c562e96f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 20:23:03 +0100 Subject: [PATCH 04/39] securityCamera.js - Camera Entity example --- examples/example/securityCamera.js | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 examples/example/securityCamera.js diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js new file mode 100644 index 0000000000..224eb9fac9 --- /dev/null +++ b/examples/example/securityCamera.js @@ -0,0 +1,55 @@ +// +// securityCamera.js +// examples/example +// +// Created by Thijs Wenker on November 4, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +const CAMERA_OFFSET = {x: 0, y: 4, z: -14}; +const LOOKAT_START_OFFSET = {x: -10, y: 0, z: 14}; +const LOOKAT_END_OFFSET = {x: 10, y: 0, z: 14}; + +var lookatTargets = [Vec3.sum(MyAvatar.position, LOOKAT_START_OFFSET), Vec3.sum(MyAvatar.position, LOOKAT_END_OFFSET)]; +var currentTarget = 0; + +var forward = true; + +var oldCameraMode = Camera.mode; + +var cameraLookAt = function(cameraPos, lookAtPos) { + var lookAtRaw = Quat.lookAt(cameraPos, lookAtPos, Vec3.UP); + lookAtRaw.w = -lookAtRaw.w; + return lookAtRaw; +}; + +cameraEntity = Entities.addEntity({ + type: "Camera", + position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) +}); + +Camera.mode = "camera entity"; +Camera.cameraEntity = cameraEntity; + +Script.update.connect(function(deltaTime) { + var cameraProperties = Entities.getEntityProperties(cameraEntity, ["position", "rotation"]); + var targetOrientation = cameraLookAt(cameraProperties.position, lookatTargets[currentTarget]); + if (Math.abs(targetOrientation.x - cameraProperties.rotation.x) < 0.01 && + Math.abs(targetOrientation.y - cameraProperties.rotation.y) < 0.01 && + Math.abs(targetOrientation.z - cameraProperties.rotation.z) < 0.01 && + Math.abs(targetOrientation.w - cameraProperties.rotation.w) < 0.01 + ) { + currentTarget = (currentTarget + 1) % lookatTargets.length; + return; + } + Entities.editEntity(cameraEntity, {rotation: Quat.mix(cameraProperties.rotation, targetOrientation, deltaTime / 3)}); +}); + +Script.scriptEnding.connect(function() { + Entities.deleteEntity(cameraEntity); + Camera.mode = oldCameraMode; + Camera.cameraEntity = null; +}); From 0ebd6e47cc33668092c67bc32c3b9150e59adb78 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 21:21:03 +0100 Subject: [PATCH 05/39] fix magic number --- examples/example/securityCamera.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js index 224eb9fac9..19c381d3df 100644 --- a/examples/example/securityCamera.js +++ b/examples/example/securityCamera.js @@ -12,6 +12,7 @@ const CAMERA_OFFSET = {x: 0, y: 4, z: -14}; const LOOKAT_START_OFFSET = {x: -10, y: 0, z: 14}; const LOOKAT_END_OFFSET = {x: 10, y: 0, z: 14}; +const TINY_VALUE = 0.001; var lookatTargets = [Vec3.sum(MyAvatar.position, LOOKAT_START_OFFSET), Vec3.sum(MyAvatar.position, LOOKAT_END_OFFSET)]; var currentTarget = 0; @@ -37,11 +38,11 @@ Camera.cameraEntity = cameraEntity; Script.update.connect(function(deltaTime) { var cameraProperties = Entities.getEntityProperties(cameraEntity, ["position", "rotation"]); var targetOrientation = cameraLookAt(cameraProperties.position, lookatTargets[currentTarget]); - if (Math.abs(targetOrientation.x - cameraProperties.rotation.x) < 0.01 && - Math.abs(targetOrientation.y - cameraProperties.rotation.y) < 0.01 && - Math.abs(targetOrientation.z - cameraProperties.rotation.z) < 0.01 && - Math.abs(targetOrientation.w - cameraProperties.rotation.w) < 0.01 - ) { + if (Math.abs(targetOrientation.x - cameraProperties.rotation.x) < TINY_VALUE && + Math.abs(targetOrientation.y - cameraProperties.rotation.y) < TINY_VALUE && + Math.abs(targetOrientation.z - cameraProperties.rotation.z) < TINY_VALUE && + Math.abs(targetOrientation.w - cameraProperties.rotation.w) < TINY_VALUE) { + currentTarget = (currentTarget + 1) % lookatTargets.length; return; } From 42cbccc725191bdf922e8ccb8ca7d6b3fad621ca Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 21:39:22 +0100 Subject: [PATCH 06/39] fix stretchy HMD view on entity camera --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eb913ce2f6..35aeba8602 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1204,13 +1204,13 @@ void Application::paintGL() { EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); if (cameraEntity != nullptr) { if (isHMDMode()) { - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(cameraEntity->getPosition() + hmdOffset); glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); _myCamera.setRotation(cameraEntity->getRotation() * hmdRotation); + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(cameraEntity->getPosition() + (hmdRotation * hmdOffset)); } else { - _myCamera.setPosition(cameraEntity->getPosition()); _myCamera.setRotation(cameraEntity->getRotation()); + _myCamera.setPosition(cameraEntity->getPosition()); } } } From de98a17ada0c21f57bfffe92a45bd67326f38399 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 6 Nov 2015 01:58:08 +0100 Subject: [PATCH 07/39] - rename CameraEntityItem to AnchorEntityItem --- examples/edit.js | 14 +++++------ examples/example/securityCamera.js | 6 ++--- examples/libraries/entityPropertyDialogBox.js | 2 +- interface/src/Application.cpp | 6 ++--- interface/src/Camera.cpp | 23 ++++++------------ interface/src/Camera.h | 4 ++-- interface/src/Menu.h | 2 +- ...eraEntityItem.cpp => AnchorEntityItem.cpp} | 24 +++++++++---------- ...{CameraEntityItem.h => AnchorEntityItem.h} | 12 +++++----- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityTypes.cpp | 4 ++-- libraries/entities/src/EntityTypes.h | 4 ++-- 12 files changed, 47 insertions(+), 56 deletions(-) rename libraries/entities/src/{CameraEntityItem.cpp => AnchorEntityItem.cpp} (76%) rename libraries/entities/src/{CameraEntityItem.h => AnchorEntityItem.h} (87%) diff --git a/examples/edit.js b/examples/edit.js index ff4021c46f..cf5163b824 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -155,7 +155,7 @@ var toolBar = (function() { newWebButton, newZoneButton, newPolyVoxButton, - newCameraButton, + newAnchorButton, browseMarketplaceButton; function initialize() { @@ -300,8 +300,8 @@ var toolBar = (function() { visible: false }); - newCameraButton = toolBar.addTool({ - imageURL: toolIconUrl + "light.svg", + newAnchorButton = toolBar.addTool({ + imageURL: toolIconUrl + "add-anchor.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, @@ -360,7 +360,7 @@ var toolBar = (function() { toolBar.showTool(newWebButton, doShow); toolBar.showTool(newZoneButton, doShow); toolBar.showTool(newPolyVoxButton, doShow); - toolBar.showTool(newCameraButton, doShow); + toolBar.showTool(newAnchorButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -619,9 +619,9 @@ var toolBar = (function() { return true; } - if (newCameraButton === toolBar.clicked(clickedOverlay)) { + if (newAnchorButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ - type: "Camera" + type: "Anchor" }); return true; @@ -1642,7 +1642,7 @@ PropertiesTool = function(opts) { } } else if (data.action == "previewCamera") { if (selectionManager.hasSelection()) { - Camera.mode = "camera entity"; + Camera.mode = "entity"; Camera.cameraEntity = selectionManager.selections[0]; } } else if (data.action == "rescaleDimensions") { diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js index 19c381d3df..15d2c03e2e 100644 --- a/examples/example/securityCamera.js +++ b/examples/example/securityCamera.js @@ -28,11 +28,11 @@ var cameraLookAt = function(cameraPos, lookAtPos) { }; cameraEntity = Entities.addEntity({ - type: "Camera", - position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) + type: "Anchor", + position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) }); -Camera.mode = "camera entity"; +Camera.mode = "entity"; Camera.cameraEntity = cameraEntity; Script.update.connect(function(deltaTime) { diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index b47e26579d..b34bec2a25 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -275,7 +275,7 @@ EntityPropertyDialogBox = (function () { Window.inlineButtonClicked.connect(function (name) { if (name == "previewCamera") { - Camera.mode = "camera entity"; + Camera.mode = "entity"; Camera.cameraEntity = propertiesForEditedEntity.id; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 35aeba8602..7d82f2269b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1200,7 +1200,7 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - } else if (_myCamera.getMode() == CAMERA_MODE_CAMERA_ENTITY) { + } else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); if (cameraEntity != nullptr) { if (isHMDMode()) { @@ -2687,8 +2687,8 @@ void Application::cameraMenuChanged() { _myCamera.setMode(CAMERA_MODE_INDEPENDENT); } } else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) { - if (_myCamera.getMode() != CAMERA_MODE_CAMERA_ENTITY) { - _myCamera.setMode(CAMERA_MODE_CAMERA_ENTITY); + if (_myCamera.getMode() != CAMERA_MODE_ENTITY) { + _myCamera.setMode(CAMERA_MODE_ENTITY); } } } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index ae654c4906..53a3500bff 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -28,8 +28,8 @@ CameraMode stringToMode(const QString& mode) { return CAMERA_MODE_MIRROR; } else if (mode == "independent") { return CAMERA_MODE_INDEPENDENT; - } else if (mode == "camera entity") { - return CAMERA_MODE_CAMERA_ENTITY; + } else if (mode == "entity") { + return CAMERA_MODE_ENTITY; } return CAMERA_MODE_NULL; } @@ -43,8 +43,8 @@ QString modeToString(CameraMode mode) { return "mirror"; } else if (mode == CAMERA_MODE_INDEPENDENT) { return "independent"; - } else if (mode == CAMERA_MODE_CAMERA_ENTITY) { - return "camera entity"; + } else if (mode == CAMERA_MODE_ENTITY) { + return "entity"; } return "unknown"; } @@ -105,17 +105,8 @@ QUuid Camera::getCameraEntity() const { return QUuid(); }; -void Camera::setCameraEntity(QUuid cameraEntityID) { - EntityItemPointer entity = qApp->getEntities()->getTree()->findEntityByID(cameraEntityID); - if (entity == nullptr) { - qDebug() << "entity pointer not found"; - return; - } - if (entity->getType() != EntityTypes::Camera) { - qDebug() << "entity type is not camera"; - return; - } - _cameraEntity = entity; +void Camera::setCameraEntity(QUuid entityID) { + _cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID); } void Camera::setProjection(const glm::mat4& projection) { @@ -142,7 +133,7 @@ void Camera::setModeString(const QString& mode) { case CAMERA_MODE_INDEPENDENT: Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); break; - case CAMERA_MODE_CAMERA_ENTITY: + case CAMERA_MODE_ENTITY: Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); break; default: diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 7aa31456e9..017bd742a4 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -24,7 +24,7 @@ enum CameraMode CAMERA_MODE_FIRST_PERSON, CAMERA_MODE_MIRROR, CAMERA_MODE_INDEPENDENT, - CAMERA_MODE_CAMERA_ENTITY, + CAMERA_MODE_ENTITY, NUM_CAMERA_MODES }; @@ -73,7 +73,7 @@ public slots: void setProjection(const glm::mat4& projection); QUuid getCameraEntity() const; - void setCameraEntity(QUuid cameraEntityID); + void setCameraEntity(QUuid entityID); PickRay computePickRay(float x, float y); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 5c84bb11fa..5964049f93 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -157,7 +157,7 @@ namespace MenuOption { const QString Bookmarks = "Bookmarks"; const QString CachesSize = "RAM Caches Size"; const QString CalibrateCamera = "Calibrate Camera"; - const QString CameraEntityMode = "Camera Entity"; + const QString CameraEntityMode = "Entity Mode"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; const QString Collisions = "Collisions"; diff --git a/libraries/entities/src/CameraEntityItem.cpp b/libraries/entities/src/AnchorEntityItem.cpp similarity index 76% rename from libraries/entities/src/CameraEntityItem.cpp rename to libraries/entities/src/AnchorEntityItem.cpp index 8370447389..d84ac3c3a5 100644 --- a/libraries/entities/src/CameraEntityItem.cpp +++ b/libraries/entities/src/AnchorEntityItem.cpp @@ -1,5 +1,5 @@ // -// CameraEntityItem.cpp +// AnchorEntityItem.cpp // libraries/entities/src // // Created by Thijs Wenker on 11/4/15. @@ -12,30 +12,30 @@ #include "EntitiesLogging.h" #include "EntityItemID.h" #include "EntityItemProperties.h" -#include "CameraEntityItem.h" +#include "AnchorEntityItem.h" -EntityItemPointer CameraEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer result { new CameraEntityItem(entityID, properties) }; +EntityItemPointer AnchorEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItemPointer result { new AnchorEntityItem(entityID, properties) }; return result; } // our non-pure virtual subclass for now... -CameraEntityItem::CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : +AnchorEntityItem::AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : EntityItem(entityItemID) { - _type = EntityTypes::Camera; + _type = EntityTypes::Anchor; setProperties(properties); } -EntityItemProperties CameraEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { +EntityItemProperties AnchorEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class return properties; } -bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { +bool AnchorEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -44,7 +44,7 @@ bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { if (wantDebug) { uint64_t now = usecTimestampNow(); int elapsed = now - getLastEdited(); - qCDebug(entities) << "CameraEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + qCDebug(entities) << "AnchorEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << "now=" << now << " getLastEdited()=" << getLastEdited(); } setLastEdited(properties._lastEdited); @@ -53,7 +53,7 @@ bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } -int CameraEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +int AnchorEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { @@ -66,12 +66,12 @@ int CameraEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data // TODO: eventually only include properties changed since the params.lastViewFrustumSent time -EntityPropertyFlags CameraEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { +EntityPropertyFlags AnchorEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); return requestedProperties; } -void CameraEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, +void AnchorEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, diff --git a/libraries/entities/src/CameraEntityItem.h b/libraries/entities/src/AnchorEntityItem.h similarity index 87% rename from libraries/entities/src/CameraEntityItem.h rename to libraries/entities/src/AnchorEntityItem.h index 6256c200c2..bd385517c3 100644 --- a/libraries/entities/src/CameraEntityItem.h +++ b/libraries/entities/src/AnchorEntityItem.h @@ -1,5 +1,5 @@ // -// CameraEntityItem.h +// AnchorEntityItem.h // libraries/entities/src // // Created by Thijs Wenker on 11/4/15. @@ -9,16 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_CameraEntityItem_h -#define hifi_CameraEntityItem_h +#ifndef hifi_AnchorEntityItem_h +#define hifi_AnchorEntityItem_h #include "EntityItem.h" -class CameraEntityItem : public EntityItem { +class AnchorEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); + AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); ALLOW_INSTANTIATION // This class can be instantiated @@ -44,4 +44,4 @@ public: }; -#endif // hifi_CameraEntityItem_h +#endif // hifi_AnchorEntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4522e4fc45..60199d9784 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -64,7 +64,7 @@ class EntityItemProperties { friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class CameraEntityItem; // TODO: consider removing this friend relationship and use public methods + friend class AnchorEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties() = default; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index b7d9077f57..f5915ae8c3 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,7 +29,7 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" -#include "CameraEntityItem.h" +#include "AnchorEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -50,7 +50,7 @@ REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(PolyLine) -REGISTER_ENTITY_TYPE(Camera); +REGISTER_ENTITY_TYPE(Anchor); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 82bbffe254..dbe9569f63 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,8 +48,8 @@ public: Line, PolyVox, PolyLine, - Camera, - LAST = Camera + Anchor, + LAST = Anchor } EntityType; static const QString& getEntityTypeName(EntityType entityType); From 7f6a20af99b3a118475c2c4dc87a9c507e55b533 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 10 Nov 2015 19:22:57 +0100 Subject: [PATCH 08/39] bump entities protocol version --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 24034ff9b3..64936c3a59 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING; + return VERSION_ENTITIES_ANCHOR; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 82d905bf28..56cb60e02b 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -146,5 +146,6 @@ const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; +const PacketVersion VERSION_ENTITIES_ANCHOR = 50; #endif // hifi_PacketHeaders_h From 6a31f76d5f4d6800062461bf39ea2797ddde7f68 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 12 Nov 2015 02:11:07 +0100 Subject: [PATCH 09/39] Revert Anchor entity addition --- libraries/entities/src/AnchorEntityItem.cpp | 84 ------------------- libraries/entities/src/AnchorEntityItem.h | 47 ----------- libraries/entities/src/EntityTypes.cpp | 2 - libraries/entities/src/EntityTypes.h | 3 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 - 6 files changed, 2 insertions(+), 137 deletions(-) delete mode 100644 libraries/entities/src/AnchorEntityItem.cpp delete mode 100644 libraries/entities/src/AnchorEntityItem.h diff --git a/libraries/entities/src/AnchorEntityItem.cpp b/libraries/entities/src/AnchorEntityItem.cpp deleted file mode 100644 index d84ac3c3a5..0000000000 --- a/libraries/entities/src/AnchorEntityItem.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// -// AnchorEntityItem.cpp -// libraries/entities/src -// -// Created by Thijs Wenker on 11/4/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "EntitiesLogging.h" -#include "EntityItemID.h" -#include "EntityItemProperties.h" -#include "AnchorEntityItem.h" - - -EntityItemPointer AnchorEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer result { new AnchorEntityItem(entityID, properties) }; - return result; -} - -// our non-pure virtual subclass for now... -AnchorEntityItem::AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID) -{ - _type = EntityTypes::Anchor; - - setProperties(properties); -} - - -EntityItemProperties AnchorEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - return properties; -} - -bool AnchorEntityItem::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - - if (somethingChanged) { - bool wantDebug = false; - if (wantDebug) { - uint64_t now = usecTimestampNow(); - int elapsed = now - getLastEdited(); - qCDebug(entities) << "AnchorEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " getLastEdited()=" << getLastEdited(); - } - setLastEdited(properties._lastEdited); - } - - return somethingChanged; -} - -int AnchorEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - return bytesRead; -} - - -// TODO: eventually only include properties changed since the params.lastViewFrustumSent time -EntityPropertyFlags AnchorEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - return requestedProperties; -} - -void AnchorEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; -} - diff --git a/libraries/entities/src/AnchorEntityItem.h b/libraries/entities/src/AnchorEntityItem.h deleted file mode 100644 index bd385517c3..0000000000 --- a/libraries/entities/src/AnchorEntityItem.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// AnchorEntityItem.h -// libraries/entities/src -// -// Created by Thijs Wenker on 11/4/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AnchorEntityItem_h -#define hifi_AnchorEntityItem_h - -#include "EntityItem.h" - -class AnchorEntityItem : public EntityItem { -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - - ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); - - // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - -}; - -#endif // hifi_AnchorEntityItem_h diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index f5915ae8c3..50b98d4b5e 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,7 +29,6 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" -#include "AnchorEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -50,7 +49,6 @@ REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(PolyLine) -REGISTER_ENTITY_TYPE(Anchor); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index dbe9569f63..30b6edbc07 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,8 +48,7 @@ public: Line, PolyVox, PolyLine, - Anchor, - LAST = Anchor + LAST = PolyLine } EntityType; static const QString& getEntityTypeName(EntityType entityType); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 64936c3a59..24034ff9b3 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_ANCHOR; + return VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 56cb60e02b..82d905bf28 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -146,6 +146,5 @@ const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; -const PacketVersion VERSION_ENTITIES_ANCHOR = 50; #endif // hifi_PacketHeaders_h From 764af63ea97aec235d8aa3e265fa2aaa616b6107 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 12 Nov 2015 02:19:08 +0100 Subject: [PATCH 10/39] remove Anchor entity from example and edit script, replace with invisible Box in example --- examples/edit.js | 23 ------------------- examples/example/securityCamera.js | 3 ++- examples/libraries/entityPropertyDialogBox.js | 6 ++--- libraries/entities/src/EntityItemProperties.h | 1 - libraries/entities/src/EntityTypes.cpp | 2 +- 5 files changed, 5 insertions(+), 30 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index cf5163b824..74551384c9 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -155,7 +155,6 @@ var toolBar = (function() { newWebButton, newZoneButton, newPolyVoxButton, - newAnchorButton, browseMarketplaceButton; function initialize() { @@ -300,20 +299,6 @@ var toolBar = (function() { visible: false }); - newAnchorButton = toolBar.addTool({ - imageURL: toolIconUrl + "add-anchor.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - that.setActive(false); } @@ -360,7 +345,6 @@ var toolBar = (function() { toolBar.showTool(newWebButton, doShow); toolBar.showTool(newZoneButton, doShow); toolBar.showTool(newPolyVoxButton, doShow); - toolBar.showTool(newAnchorButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -617,13 +601,6 @@ var toolBar = (function() { } - return true; - } - if (newAnchorButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Anchor" - }); - return true; } diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js index 15d2c03e2e..6f5ca549cd 100644 --- a/examples/example/securityCamera.js +++ b/examples/example/securityCamera.js @@ -28,7 +28,8 @@ var cameraLookAt = function(cameraPos, lookAtPos) { }; cameraEntity = Entities.addEntity({ - type: "Anchor", + type: "Box", + visible: false, position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) }); diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index b34bec2a25..39ffbed629 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -261,10 +261,8 @@ EntityPropertyDialogBox = (function () { index++; } - if (properties.type == "Camera") { - array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" }); - index++; - } + array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" }); + index++; array.push({ button: "Cancel" }); index++; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 1f08789cdc..84a5aeca5d 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -64,7 +64,6 @@ class EntityItemProperties { friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class AnchorEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties() = default; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 50b98d4b5e..52c2242629 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -48,7 +48,7 @@ REGISTER_ENTITY_TYPE(ParticleEffect) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) -REGISTER_ENTITY_TYPE(PolyLine) +REGISTER_ENTITY_TYPE(PolyLine); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); From ac635336b79ae5e77df76b1c86ab3e3d2970fa89 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 14:58:17 -0800 Subject: [PATCH 11/39] split AvatarActionHold's finding of its location target into a new function --- interface/src/avatar/AvatarActionHold.cpp | 48 ++++++++++++----------- interface/src/avatar/AvatarActionHold.h | 5 +++ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 839f2d4fbb..ca7fcfb187 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -10,7 +10,6 @@ // #include "QVariantGLM.h" -#include "avatar/MyAvatar.h" #include "avatar/AvatarManager.h" #include "AvatarActionHold.h" @@ -22,8 +21,7 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit _relativePosition(glm::vec3(0.0f)), _relativeRotation(glm::quat()), _hand("right"), - _holderID(QUuid()) -{ + _holderID(QUuid()) { _type = ACTION_TYPE_HOLD; #if WANT_DEBUG qDebug() << "AvatarActionHold::AvatarActionHold"; @@ -36,13 +34,10 @@ AvatarActionHold::~AvatarActionHold() { #endif } -void AvatarActionHold::updateActionWorker(float deltaTimeStep) { - bool gotLock = false; - glm::quat rotation; - glm::vec3 position; +std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) { std::shared_ptr holdingAvatar = nullptr; - gotLock = withTryReadLock([&]{ + withTryReadLock([&]{ QSharedPointer avatarManager = DependencyManager::get(); AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); holdingAvatar = std::static_pointer_cast(holdingAvatarData); @@ -65,22 +60,28 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { } }); + return holdingAvatar; +} + +void AvatarActionHold::updateActionWorker(float deltaTimeStep) { + glm::quat rotation; + glm::vec3 position; + std::shared_ptr holdingAvatar = getTarget(rotation, position); + if (holdingAvatar) { + bool gotLock = withTryWriteLock([&]{ + _positionalTarget = position; + _rotationalTarget = rotation; + _positionalTargetSet = true; + _rotationalTargetSet = true; + _active = true; + }); if (gotLock) { - gotLock = withTryWriteLock([&]{ - _positionalTarget = position; - _rotationalTarget = rotation; - _positionalTargetSet = true; - _rotationalTargetSet = true; - _active = true; - }); - if (gotLock) { - if (_kinematic) { - doKinematicUpdate(deltaTimeStep); - } else { - activateBody(); - ObjectActionSpring::updateActionWorker(deltaTimeStep); - } + if (_kinematic) { + doKinematicUpdate(deltaTimeStep); + } else { + activateBody(); + ObjectActionSpring::updateActionWorker(deltaTimeStep); } } } @@ -109,7 +110,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { if (_previousSet) { // smooth velocity over 2 frames glm::vec3 positionalDelta = _positionalTarget - _previousPositionalTarget; - glm::vec3 positionalVelocity = (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep); + glm::vec3 positionalVelocity = + (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep); rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); _previousPositionalDelta = positionalDelta; _previousDeltaTimeStep = deltaTimeStep; diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 15a096d1ce..b8b1a64e84 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -17,6 +17,9 @@ #include #include +#include "avatar/MyAvatar.h" + + class AvatarActionHold : public ObjectActionSpring { public: AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity); @@ -32,6 +35,8 @@ public: virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); } + std::shared_ptr getTarget(glm::quat& rotation, glm::vec3& position); + private: static const uint16_t holdVersion; From b0d24be58fc854da97ee94625a6dad2dc864b5ed Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 16:02:39 -0800 Subject: [PATCH 12/39] add a way to get a list of all actions of a certain type from an entity. hold actions average their positional targets. --- interface/src/avatar/AvatarActionHold.cpp | 29 +++++++++++++++++-- .../entities/src/EntityActionInterface.h | 10 +++++-- libraries/entities/src/EntityItem.cpp | 15 ++++++++++ libraries/entities/src/EntityItem.h | 5 +++- libraries/entities/src/EntityTypes.h | 4 +-- libraries/physics/src/ObjectAction.h | 1 - 6 files changed, 56 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index ca7fcfb187..8e13fa8385 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -66,9 +66,34 @@ std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::ve void AvatarActionHold::updateActionWorker(float deltaTimeStep) { glm::quat rotation; glm::vec3 position; - std::shared_ptr holdingAvatar = getTarget(rotation, position); + bool valid = false; + int holdCount = 0; + + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return; + } + QList holdActions = ownerEntity->getActionsOfType(ACTION_TYPE_HOLD); + foreach (EntityActionPointer action, holdActions) { + std::shared_ptr holdAction = std::static_pointer_cast(action); + glm::quat rotationForAction; + glm::vec3 positionForAction; + std::shared_ptr holdingAvatar = holdAction->getTarget(rotationForAction, positionForAction); + if (holdingAvatar) { + holdCount ++; + if (holdAction.get() == this) { + // only use the rotation for this action + valid = true; + rotation = rotationForAction; + } + + position += positionForAction; + } + } + + if (valid && holdCount > 0) { + position /= holdCount; - if (holdingAvatar) { bool gotLock = withTryWriteLock([&]{ _positionalTarget = position; _rotationalTarget = rotation; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 01292e3840..a192661e52 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -12,11 +12,14 @@ #ifndef hifi_EntityActionInterface_h #define hifi_EntityActionInterface_h +#include #include +#include -#include "EntityItem.h" - +class EntityItem; class EntitySimulation; +using EntityItemPointer = std::shared_ptr; +using EntityItemWeakPointer = std::weak_ptr; enum EntityActionType { // keep these synchronized with actionTypeFromString and actionTypeToString @@ -34,6 +37,8 @@ public: const QUuid& getID() const { return _id; } EntityActionType getType() const { return _type; } + bool isActive() { return _active; } + virtual void removeFromSimulation(EntitySimulation* simulation) const = 0; virtual EntityItemWeakPointer getOwnerEntity() const = 0; virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0; @@ -81,6 +86,7 @@ protected: QUuid _id; EntityActionType _type; + bool _active { false }; }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f032dcd347..100f6dfe22 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1844,3 +1844,18 @@ bool EntityItem::shouldSuppressLocationEdits() const { return false; } + +QList EntityItem::getActionsOfType(EntityActionType typeToGet) { + QList result; + + QHash::const_iterator i = _objectActions.begin(); + while (i != _objectActions.end()) { + EntityActionPointer action = i.value(); + if (action->getType() == typeToGet && action->isActive()) { + result += action; + } + i++; + } + + return result; +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 5b47198e97..5ceccef4b1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -30,6 +30,7 @@ #include "EntityTypes.h" #include "SimulationOwner.h" #include "SimulationFlags.h" +#include "EntityActionInterface.h" class EntitySimulation; class EntityTreeElement; @@ -419,7 +420,9 @@ public: void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; } const QUuid& getSourceUUID() const { return _sourceUUID; } - bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; } + bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; } + + QList getActionsOfType(EntityActionType typeToGet); protected: diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 30b6edbc07..3536327d18 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -20,8 +20,8 @@ #include // for RenderArgs class EntityItem; -typedef std::shared_ptr EntityItemPointer; -typedef std::weak_ptr EntityItemWeakPointer; +using EntityItemPointer = std::shared_ptr; +using EntityItemWeakPointer = std::weak_ptr; inline uint qHash(const EntityItemPointer& a, uint seed) { return qHash(a.get(), seed); diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 45b40a9fb3..afb6745e9c 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -67,7 +67,6 @@ protected: EntityItemWeakPointer _ownerEntity; QString _tag; quint64 _expires { 0 }; // in seconds since epoch - bool _active { false }; private: int getEntityServerClockSkew() const; From 71cc36fdd8771ccd17d874e691a4734518378d2b Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 16 Nov 2015 18:31:24 -0800 Subject: [PATCH 13/39] removing ms to seconds conversion in MyAvatar --- interface/src/scripting/RecordingScriptingInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index bf585f5481..32bd6fde97 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -56,11 +56,11 @@ bool RecordingScriptingInterface::isPaused() { } float RecordingScriptingInterface::playerElapsed() { - return (float)_player->position() / MSECS_PER_SECOND; + return _player->position(); } float RecordingScriptingInterface::playerLength() { - return _player->length() / MSECS_PER_SECOND; + return _player->length(); } void RecordingScriptingInterface::loadRecording(const QString& filename) { @@ -103,7 +103,7 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) { } void RecordingScriptingInterface::setPlayerTime(float time) { - _player->seek(time * MSECS_PER_SECOND); + _player->seek(time); } void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurrentLocation) { From 8553033d59273b2385687c4afe35d8c01b17ef2f Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 16 Nov 2015 19:27:28 -0800 Subject: [PATCH 14/39] Refactoring the mess for the Agent --- assignment-client/src/Agent.cpp | 2 ++ examples/acScripts/ControlACs.js | 2 +- interface/src/scripting/RecordingScriptingInterface.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 063bf24de8..3cefbbe246 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -28,6 +28,7 @@ #include // TODO: consider moving to scriptengine.h #include "avatars/ScriptableAvatar.h" +#include "RecordingScriptingInterface.h" #include "Agent.h" @@ -45,6 +46,7 @@ Agent::Agent(NLPacket& packet) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index ba066d9750..e3d63cc26e 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -156,7 +156,7 @@ function sendCommand(id, action) { position: controlEntityPosition, dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, visible: false, - lifetime: 10, + lifetime: 10000, userData: JSON.stringify({ idKey: { uD_id: id diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index 32bd6fde97..1cb29feae4 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -8,6 +8,8 @@ #include "RecordingScriptingInterface.h" +#include + #include #include #include @@ -16,8 +18,8 @@ #include #include -#include "avatar/AvatarManager.h" -#include "Application.h" +//#include "avatar/AvatarManager.h" +//#include "Application.h" #include "InterfaceLogging.h" typedef int16_t AudioSample; From 5b92929833a4a16a714ee686923f6147a1901cc1 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 17 Nov 2015 09:32:22 -0800 Subject: [PATCH 15/39] Moving the REcordingScriptingInterface into the script-engine and wiiring up in the assignment-client --- assignment-client/src/Agent.cpp | 8 ++++- interface/src/Application.cpp | 7 ++-- .../src}/RecordingScriptingInterface.cpp | 36 ++++++++++++------- .../src}/RecordingScriptingInterface.h | 3 ++ libraries/script-engine/src/ScriptEngine.cpp | 4 +++ 5 files changed, 42 insertions(+), 16 deletions(-) rename {interface/src/scripting => libraries/script-engine/src}/RecordingScriptingInterface.cpp (85%) rename {interface/src/scripting => libraries/script-engine/src}/RecordingScriptingInterface.h (96%) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 3cefbbe246..2c4d190f73 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -24,6 +24,9 @@ #include #include +#include +#include + #include #include // TODO: consider moving to scriptengine.h @@ -46,6 +49,8 @@ Agent::Agent(NLPacket& packet) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); @@ -168,7 +173,7 @@ void Agent::run() { // give this AvatarData object to the script engine setAvatarData(&scriptedAvatar, "Avatar"); - + auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); @@ -245,6 +250,7 @@ void Agent::setIsAvatar(bool isAvatar) { void Agent::setAvatarData(AvatarData* avatarData, const QString& objectName) { _avatarData = avatarData; _scriptEngine->registerGlobalObject(objectName, avatarData); + DependencyManager::get()->setControlledAvatar(avatarData); } void Agent::sendAvatarIdentityPacket() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96b8ab74a8..ee25d6a06a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -86,6 +86,7 @@ #include #include #include +#include #include #include #include @@ -126,7 +127,6 @@ #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" -#include "scripting/RecordingScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" @@ -733,6 +733,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); + // Assign MyAvatar to th eRecording Singleton + DependencyManager::get()->setControlledAvatar(getMyAvatar()); + + // Now that menu is initalized we can sync myAvatar with it's state. getMyAvatar()->updateMotionBehaviorFromMenu(); @@ -3979,7 +3983,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri RayToOverlayIntersectionResultFromScriptValue); scriptEngine->registerGlobalObject("Desktop", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Recording", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp similarity index 85% rename from interface/src/scripting/RecordingScriptingInterface.cpp rename to libraries/script-engine/src/RecordingScriptingInterface.cpp index 1cb29feae4..0973f6870f 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -15,12 +15,12 @@ #include #include #include -#include +//#include #include - +#include //#include "avatar/AvatarManager.h" //#include "Application.h" -#include "InterfaceLogging.h" +#include "ScriptEngineLogging.h" typedef int16_t AudioSample; @@ -45,8 +45,12 @@ RecordingScriptingInterface::RecordingScriptingInterface() { _player = DependencyManager::get(); _recorder = DependencyManager::get(); - auto audioClient = DependencyManager::get(); - connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); +// auto audioClient = DependencyManager::get(); + // connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); +} + +void RecordingScriptingInterface::setControlledAvatar(AvatarData* avatar) { + _controlledAvatar = avatar; } bool RecordingScriptingInterface::isPlaying() { @@ -86,7 +90,8 @@ void RecordingScriptingInterface::startPlaying() { QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); return; } - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; // Playback from the current position if (_playFromCurrentLocation) { _dummyAvatar.setRecordingBasis(std::make_shared(myAvatar->getTransform())); @@ -154,7 +159,7 @@ float RecordingScriptingInterface::recorderElapsed() { void RecordingScriptingInterface::startRecording() { if (_recorder->isRecording()) { - qCWarning(interfaceapp) << "Recorder is already running"; + qCWarning(scriptengine) << "Recorder is already running"; return; } @@ -165,7 +170,9 @@ void RecordingScriptingInterface::startRecording() { _recordingEpoch = Frame::epochForFrameTime(0); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; myAvatar->setRecordingBasis(); _recorder->start(); } @@ -182,7 +189,9 @@ void RecordingScriptingInterface::stopRecording() { } _lastClip->seek(0); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; myAvatar->clearRecordingBasis(); } @@ -208,7 +217,7 @@ void RecordingScriptingInterface::loadLastRecording() { } if (!_lastClip) { - qCDebug(interfaceapp) << "There is no recording to load"; + qCDebug(scriptengine) << "There is no recording to load"; return; } @@ -221,7 +230,8 @@ void RecordingScriptingInterface::processAvatarFrame(const Frame::ConstPointer& AvatarData::fromFrame(frame->data, _dummyAvatar); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; if (_useHeadModel && _dummyAvatar.getFaceModelURL().isValid() && (_dummyAvatar.getFaceModelURL() != myAvatar->getFaceModelURL())) { // FIXME @@ -255,6 +265,6 @@ void RecordingScriptingInterface::processAudioInput(const QByteArray& audio) { } void RecordingScriptingInterface::processAudioFrame(const recording::FrameConstPointer& frame) { - auto audioClient = DependencyManager::get(); - audioClient->handleRecordedAudioInput(frame->data); + // auto audioClient = DependencyManager::get(); + // audioClient->handleRecordedAudioInput(frame->data); } diff --git a/interface/src/scripting/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h similarity index 96% rename from interface/src/scripting/RecordingScriptingInterface.h rename to libraries/script-engine/src/RecordingScriptingInterface.h index 510a4b6898..db3a6b1240 100644 --- a/interface/src/scripting/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -24,6 +24,8 @@ class RecordingScriptingInterface : public QObject, public Dependency { public: RecordingScriptingInterface(); + void setControlledAvatar(AvatarData* avatar); + public slots: bool isPlaying(); bool isPaused(); @@ -74,6 +76,7 @@ private: Flag _useSkeletonModel { false }; recording::ClipPointer _lastClip; AvatarData _dummyAvatar; + AvatarData* _controlledAvatar; }; #endif // hifi_RecordingScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f62bf8cd5..f30a5d4234 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -46,6 +46,7 @@ #include "WebSocketClass.h" #include "SceneScriptingInterface.h" +#include "RecordingScriptingInterface.h" #include "MIDIEvent.h" @@ -375,6 +376,9 @@ void ScriptEngine::init() { auto scriptingInterface = DependencyManager::get(); registerGlobalObject("Controller", scriptingInterface.data()); UserInputMapper::registerControllerTypes(this); + + auto recordingInterface = DependencyManager::get(); + registerGlobalObject("Recording", recordingInterface.data()); } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { From b062d23f612e780abee293bf5483d53a2b5ade33 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 09:51:52 -0800 Subject: [PATCH 16/39] bumper is now an equip toggle --- examples/controllers/handControllerGrab.js | 103 ++++++++++++++++++--- examples/libraries/utils.js | 10 +- libraries/entities/src/EntityTree.cpp | 8 ++ 3 files changed, 105 insertions(+), 16 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index d308b3dc49..c50a1418bd 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -28,6 +28,8 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value var TRIGGER_ON_VALUE = 0.4; var TRIGGER_OFF_VALUE = 0.15; +var BUMPER_ON_VALUE = 0.5; + // // distant manipulation // @@ -106,6 +108,11 @@ var STATE_CONTINUE_NEAR_TRIGGER = 7; var STATE_FAR_TRIGGER = 8; var STATE_CONTINUE_FAR_TRIGGER = 9; var STATE_RELEASE = 10; +var STATE_EQUIP_SEARCHING = 11; +var STATE_EQUIP = 12 +var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down +var STATE_CONTINUE_EQUIP = 14; +var STATE_WAITING_FOR_BUMPER_RELEASE = 15; function stateToName(state) { @@ -132,6 +139,16 @@ function stateToName(state) { return "continue_far_trigger"; case STATE_RELEASE: return "release"; + case STATE_EQUIP_SEARCHING: + return "equip_searching"; + case STATE_EQUIP: + return "equip"; + case STATE_CONTINUE_EQUIP_BD: + return "continue_equip_bd"; + case STATE_CONTINUE_EQUIP: + return "continue_equip"; + case STATE_WAITING_FOR_BUMPER_RELEASE: + return "waiting_for_bumper_release"; } return "unknown"; @@ -182,6 +199,7 @@ function MyController(hand) { this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; + this.rawBumperValue = 0; this.offsetPosition = { x: 0.0, y: 0.0, z: 0.0 }; this.offsetRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }; @@ -200,6 +218,9 @@ function MyController(hand) { case STATE_SEARCHING: this.search(); break; + case STATE_EQUIP_SEARCHING: + this.search(); + break; case STATE_DISTANCE_HOLDING: this.distanceHolding(); break; @@ -209,7 +230,15 @@ function MyController(hand) { case STATE_NEAR_GRABBING: this.nearGrabbing(); break; + case STATE_EQUIP: + this.nearGrabbing(); + break; + case STATE_WAITING_FOR_BUMPER_RELEASE: + this.waitingForBumperRelease(); + break; case STATE_CONTINUE_NEAR_GRABBING: + case STATE_CONTINUE_EQUIP_BD: + case STATE_CONTINUE_EQUIP: this.continueNearGrabbing(); break; case STATE_NEAR_TRIGGER: @@ -281,10 +310,15 @@ function MyController(hand) { this.pointer = null; }; - this.eitherTrigger = function (value) { + this.triggerPress = function (value) { _this.rawTriggerValue = value; }; + this.bumperPress = function (value) { + _this.rawBumperValue = value; + }; + + this.updateSmoothedTrigger = function () { var triggerValue = this.rawTriggerValue; // smooth out trigger value @@ -305,23 +339,37 @@ function MyController(hand) { return triggerValue > TRIGGER_ON_VALUE; }; + this.bumperSqueezed = function() { + return _this.rawBumperValue > BUMPER_ON_VALUE; + } + + this.bumperReleased = function() { + return _this.rawBumperValue < BUMPER_ON_VALUE; + } + + this.off = function() { if (this.triggerSmoothedSqueezed()) { this.lastPickTime = 0; this.setState(STATE_SEARCHING); return; } + if (this.bumperSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_EQUIP_SEARCHING); + return; + } } this.search = function() { this.grabbedEntity = null; - //if this hand is the one that's disabled, we don't want to search for anything at all + // if this hand is the one that's disabled, we don't want to search for anything at all if (this.hand === disabledHand) { return; } - if (this.triggerSmoothedReleased()) { + if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { this.setState(STATE_RELEASE); return; } @@ -398,7 +446,7 @@ function MyController(hand) { return; } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; - this.setState(STATE_NEAR_GRABBING); + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) return; } } else if (! entityIsGrabbedByOther(intersection.entityID)) { @@ -407,8 +455,14 @@ function MyController(hand) { && !intersection.properties.locked) { // the hand is far from the intersected object. go into distance-holding mode this.grabbedEntity = intersection.entityID; - this.setState(STATE_DISTANCE_HOLDING); - return; + if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { + // if a distance pick in equip mode hits something with a spatialKey, equip it + this.setState(STATE_EQUIP); + return; + } else if (this.state == STATE_SEARCHING) { + this.setState(STATE_DISTANCE_HOLDING); + return; + } } else if (grabbableData.wantsTrigger) { this.grabbedEntity = intersection.entityID; this.setState(STATE_FAR_TRIGGER); @@ -434,6 +488,7 @@ function MyController(hand) { var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); var minDistance = PICK_MAX_DISTANCE; var i, props, distance, grabbableData; + this.grabbedEntity = null; for (i = 0; i < nearbyEntities.length; i++) { var grabbableDataForCandidate = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); @@ -490,7 +545,7 @@ function MyController(hand) { this.setState(STATE_NEAR_TRIGGER); return; } else if (!props.locked && props.collisionsWillMove) { - this.setState(STATE_NEAR_GRABBING); + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) return; } }; @@ -634,13 +689,12 @@ function MyController(hand) { return; } - if (this.triggerSmoothedReleased()) { + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); return; } - this.lineOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); @@ -686,7 +740,7 @@ function MyController(hand) { this.actionID = null; } else { this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - this.setState(STATE_CONTINUE_NEAR_GRABBING); + this.setState(this.state == STATE_NEAR_GRABBING ? STATE_CONTINUE_NEAR_GRABBING : STATE_CONTINUE_EQUIP_BD) if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { @@ -696,17 +750,26 @@ function MyController(hand) { } - this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;; + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; this.currentObjectTime = Date.now(); }; this.continueNearGrabbing = function() { - if (this.triggerSmoothedReleased()) { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); return; } + if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { + this.setState(STATE_CONTINUE_EQUIP); + return; + } + if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { + this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); + return; + } // Keep track of the fingertip velocity to impart when we release the object. // Note that the idea of using a constant 'tip' velocity regardless of the @@ -740,6 +803,13 @@ function MyController(hand) { } }; + this.waitingForBumperRelease = function() { + if (this.bumperReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + } + } + this.nearTrigger = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); @@ -919,6 +989,7 @@ function MyController(hand) { } Entities.editEntity(entityID, whileHeldProperties); } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); return data; }; @@ -948,8 +1019,12 @@ var leftController = new MyController(LEFT_HAND); var MAPPING_NAME = "com.highfidelity.handControllerGrab"; var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.RB, Controller.Standard.RT]).peek().to(rightController.eitherTrigger); -mapping.from([Controller.Standard.LB, Controller.Standard.LT]).peek().to(leftController.eitherTrigger); +mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); +mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); + +mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); +mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); + Controller.enableMapping(MAPPING_NAME); diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 25900471c1..5d14bfb7dd 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -11,6 +11,12 @@ vec3toStr = function(v, digits) { return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }"; } +quatToStr = function(q, digits) { + if (!digits) { digits = 3; } + return "{ " + q.w.toFixed(digits) + ", " + q.x.toFixed(digits) + ", " + + q.y.toFixed(digits) + ", " + q.z.toFixed(digits)+ " }"; +} + vec3equal = function(v0, v1) { return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z); } @@ -51,7 +57,7 @@ addLine = function(origin, vector, color) { // FIXME fetch from a subkey of user data to support non-destructive modifications setEntityUserData = function(id, data) { var json = JSON.stringify(data) - Entities.editEntity(id, { userData: json }); + Entities.editEntity(id, { userData: json }); } // FIXME do non-destructive modification of the existing user data @@ -60,7 +66,7 @@ getEntityUserData = function(id) { var properties = Entities.getEntityProperties(id, "userData"); if (properties.userData) { try { - results = JSON.parse(properties.userData); + results = JSON.parse(properties.userData); } catch(err) { logDebug(err); logDebug(properties.userData); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 027972549a..dc7c19056a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -704,6 +704,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { + QString changeHint = properties.getUserData(); + changedProperties[index] = QString("userData:") + changeHint; + } + } } int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, From d21a2fee203b087d952bd6f76696668978a175ff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 13:12:22 -0800 Subject: [PATCH 17/39] don't use spatial-key for normal grab, only equip. allow switching from a near or far grab to an equip. --- examples/controllers/handControllerGrab.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index c50a1418bd..add809e73b 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -606,6 +606,16 @@ function MyController(hand) { var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && + typeof grabbableData.spatialKey !== 'undefined') { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.setState(STATE_EQUIP); + this.grabbedEntity = saveGrabbedID; + return; + } this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); @@ -710,7 +720,8 @@ function MyController(hand) { var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - if (grabbableData.spatialKey) { + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. if (grabbableData.spatialKey.relativePosition) { this.offsetPosition = grabbableData.spatialKey.relativePosition; } @@ -770,6 +781,10 @@ function MyController(hand) { this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); return; } + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { + this.setState(STATE_CONTINUE_EQUIP_BD); + return; + } // Keep track of the fingertip velocity to impart when we release the object. // Note that the idea of using a constant 'tip' velocity regardless of the From c6051bb3253234113d2323b9ca328e810b1a0bfc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 14:58:20 -0800 Subject: [PATCH 18/39] reduce size of near-grab radius. don't draw pick laser until we know we aren't going to do a near grab --- examples/controllers/handControllerGrab.js | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index add809e73b..973ca53cf6 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -47,7 +47,7 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray // near grabbing // -var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected +var GRAB_RADIUS = 0.01; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected @@ -382,8 +382,6 @@ function MyController(hand) { length: PICK_MAX_DISTANCE }; - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - // don't pick 60x per second. var pickRays = []; var now = Date.now(); @@ -538,16 +536,17 @@ function MyController(hand) { grabbableData = grabbableDataForCandidate; } } - if (this.grabbedEntity === null) { - return; - } - if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) - return; + if (this.grabbedEntity !== null) { + if (grabbableData.wantsTrigger) { + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!props.locked && props.collisionsWillMove) { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + return; + } } + + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); }; this.distanceHolding = function() { From 5ae3c5aea0429014863b018550a0137616540d2b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 14:59:06 -0800 Subject: [PATCH 19/39] adjust size of near-grab radius. --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 973ca53cf6..efd288c751 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -47,7 +47,7 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray // near grabbing // -var GRAB_RADIUS = 0.01; // if the ray misses but an object is this close, it will still be selected +var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected From 900f425f359fe59300d109bb1b96523720ba61c3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 11:58:46 -0800 Subject: [PATCH 20/39] Recording fixes --- .../scripting/RecordingScriptingInterface.cpp | 15 +- libraries/audio-client/src/AudioClient.cpp | 176 +++++++++--------- libraries/audio-client/src/AudioClient.h | 1 + libraries/avatars/src/AvatarData.cpp | 11 +- 4 files changed, 94 insertions(+), 109 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index 32bd6fde97..4ca3881e8d 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -162,26 +162,15 @@ void RecordingScriptingInterface::startRecording() { } _recordingEpoch = Frame::epochForFrameTime(0); - - auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->setRecordingBasis(); + DependencyManager::get()->getMyAvatar()->setRecordingBasis(); _recorder->start(); } void RecordingScriptingInterface::stopRecording() { _recorder->stop(); - _lastClip = _recorder->getClip(); - // post-process the audio into discreet chunks based on times of received samples _lastClip->seek(0); - Frame::ConstPointer frame; - while (frame = _lastClip->nextFrame()) { - qDebug() << "Frame time " << frame->timeOffset << " size " << frame->data.size(); - } - _lastClip->seek(0); - - auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->clearRecordingBasis(); + DependencyManager::get()->getMyAvatar()->clearRecordingBasis(); } void RecordingScriptingInterface::saveRecording(const QString& filename) { diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 50bfd995f2..d4e571ade5 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -743,19 +743,9 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } void AudioClient::handleAudioInput() { - if (!_audioPacket) { - // we don't have an audioPacket yet - set that up now - _audioPacket = NLPacket::create(PacketType::MicrophoneAudioNoEcho); - } - const float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(); - const int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); - - static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); - int16_t* const networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes); - QByteArray inputByteArray = _inputDevice->readAll(); // Add audio source injection if enabled @@ -784,30 +774,30 @@ void AudioClient::handleAudioInput() { float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); _stats.updateInputMsecsRead(audioInputMsecsRead); - while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { + const int numNetworkBytes = _isStereoInput + ? AudioConstants::NETWORK_FRAME_BYTES_STEREO + : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + const int numNetworkSamples = _isStereoInput + ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO + : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; - const int numNetworkBytes = _isStereoInput - ? AudioConstants::NETWORK_FRAME_BYTES_STEREO - : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - const int numNetworkSamples = _isStereoInput - ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO - : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; + static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; + + while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { if (!_muted) { - // zero out the monoAudioSamples array and the locally injected audio - memset(networkAudioSamples, 0, numNetworkBytes); // Increment the time since the last clip if (_timeSinceLastClip >= 0.0f) { - _timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE; + _timeSinceLastClip += (float)numNetworkSamples / (float)AudioConstants::SAMPLE_RATE; } _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); possibleResampling(_inputToNetworkResampler, - inputAudioSamples.get(), networkAudioSamples, - inputSamplesRequired, numNetworkSamples, - _inputFormat, _desiredInputFormat); + inputAudioSamples.get(), networkAudioSamples, + inputSamplesRequired, numNetworkSamples, + _inputFormat, _desiredInputFormat); // Remove DC offset if (!_isStereoInput && !_audioSourceInjectEnabled) { @@ -829,7 +819,7 @@ void AudioClient::handleAudioInput() { for (int i = 0; i < numNetworkSamples; i++) { int thisSample = std::abs(networkAudioSamples[i]); - loudness += (float) thisSample; + loudness += (float)thisSample; if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) { _timeSinceLastClip = 0.0f; @@ -839,7 +829,7 @@ void AudioClient::handleAudioInput() { _lastInputLoudness = fabs(loudness / numNetworkSamples); } - emit inputReceived({reinterpret_cast(networkAudioSamples), numNetworkBytes}); + emit inputReceived({ reinterpret_cast(networkAudioSamples), numNetworkBytes }); } else { // our input loudness is 0, since we're muted @@ -849,14 +839,38 @@ void AudioClient::handleAudioInput() { _inputRingBuffer.shiftReadPosition(inputSamplesRequired); } - auto nodeList = DependencyManager::get(); - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + emitAudioPacket(networkAudioSamples); + } +} - if (audioMixer && audioMixer->getActiveSocket()) { - glm::vec3 headPosition = _positionGetter(); - glm::quat headOrientation = _orientationGetter(); - quint8 isStereo = _isStereoInput ? 1 : 0; +void AudioClient::emitAudioPacket(const int16_t* audioData, PacketType packetType) { + static std::mutex _mutex; + using Locker = std::unique_lock; + // FIXME recorded audio isn't guaranteed to have the same stereo state + // as the current system + const int numNetworkBytes = _isStereoInput + ? AudioConstants::NETWORK_FRAME_BYTES_STEREO + : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + const int numNetworkSamples = _isStereoInput + ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO + : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; + + auto nodeList = DependencyManager::get(); + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + + if (audioMixer && audioMixer->getActiveSocket()) { + Locker lock(_mutex); + if (!_audioPacket) { + // we don't have an audioPacket yet - set that up now + _audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho); + } + + glm::vec3 headPosition = _positionGetter(); + glm::quat headOrientation = _orientationGetter(); + quint8 isStereo = _isStereoInput ? 1 : 0; + + if (packetType == PacketType::Unknown) { if (_lastInputLoudness == 0) { _audioPacket->setType(PacketType::SilentAudioFrame); } else { @@ -866,70 +880,52 @@ void AudioClient::handleAudioInput() { _audioPacket->setType(PacketType::MicrophoneAudioNoEcho); } } - - // reset the audio packet so we can start writing - _audioPacket->reset(); - - // write sequence number - _audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber); - - if (_audioPacket->getType() == PacketType::SilentAudioFrame) { - // pack num silent samples - quint16 numSilentSamples = numNetworkSamples; - _audioPacket->writePrimitive(numSilentSamples); - } else { - // set the mono/stereo byte - _audioPacket->writePrimitive(isStereo); - } - - // pack the three float positions - _audioPacket->writePrimitive(headPosition); - - // pack the orientation - _audioPacket->writePrimitive(headOrientation); - - if (_audioPacket->getType() != PacketType::SilentAudioFrame) { - // audio samples have already been packed (written to networkAudioSamples) - _audioPacket->setPayloadSize(_audioPacket->getPayloadSize() + numNetworkBytes); - } - - _stats.sentPacket(); - - nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); - - nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer); - - _outgoingAvatarAudioSequenceNumber++; + } else { + _audioPacket->setType(packetType); } + + // reset the audio packet so we can start writing + _audioPacket->reset(); + + // write sequence number + _audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber); + + if (_audioPacket->getType() == PacketType::SilentAudioFrame) { + // pack num silent samples + quint16 numSilentSamples = numNetworkSamples; + _audioPacket->writePrimitive(numSilentSamples); + } else { + // set the mono/stereo byte + _audioPacket->writePrimitive(isStereo); + } + + // pack the three float positions + _audioPacket->writePrimitive(headPosition); + + // pack the orientation + _audioPacket->writePrimitive(headOrientation); + + if (_audioPacket->getType() != PacketType::SilentAudioFrame) { + // audio samples have already been packed (written to networkAudioSamples) + _audioPacket->setPayloadSize(_audioPacket->getPayloadSize() + numNetworkBytes); + } + + static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); + int16_t* const networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes); + memcpy(networkAudioSamples, audioData, numNetworkBytes); + + _stats.sentPacket(); + + nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); + + nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer); + + _outgoingAvatarAudioSequenceNumber++; } } void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { - if (!_audioPacket) { - // we don't have an audioPacket yet - set that up now - _audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho); - } - - // FIXME either discard stereo in the recording or record a stereo flag - - auto nodeList = DependencyManager::get(); - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - if (audioMixer && audioMixer->getActiveSocket()) { - glm::vec3 headPosition = _positionGetter(); - glm::quat headOrientation = _orientationGetter(); - quint8 isStereo = _isStereoInput ? 1 : 0; - _audioPacket->reset(); - _audioPacket->setType(PacketType::MicrophoneAudioWithEcho); - _audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber); - _audioPacket->writePrimitive(isStereo); - _audioPacket->writePrimitive(headPosition); - _audioPacket->writePrimitive(headOrientation); - _audioPacket->write(audio); - _stats.sentPacket(); - nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); - nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer); - _outgoingAvatarAudioSequenceNumber++; - } + emitAudioPacket((int16_t*)audio.data(), PacketType::MicrophoneAudioWithEcho); } void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 7d2b5a783f..9d46ad9d26 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -212,6 +212,7 @@ protected: } private: + void emitAudioPacket(const int16_t* audioData, PacketType packetType = PacketType::Unknown); void outputFormatChanged(); QByteArray firstInputFrame; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 017ef7578a..fdfc6c1893 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1443,14 +1443,10 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) { auto recordingBasis = avatar.getRecordingBasis(); if (recordingBasis) { + root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); // Find the relative transform auto relativeTransform = recordingBasis->relativeTransform(avatar.getTransform()); - - // if the resulting relative basis is identity, we shouldn't record anything - if (!relativeTransform.isIdentity()) { - root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform); - root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); - } + root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform); } else { root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatar.getTransform()); } @@ -1484,6 +1480,9 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) { void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { QJsonDocument doc = QJsonDocument::fromBinaryData(frameData); +#ifdef WANT_JSON_DEBUG + qDebug() << doc.toJson(QJsonDocument::JsonFormat::Indented); +#endif QJsonObject root = doc.object(); if (root.contains(JSON_AVATAR_HEAD_MODEL)) { From 48b0465e56641daa06af5fbb92eeae69f5dbcec7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 13:33:10 -0800 Subject: [PATCH 21/39] Fixing race condition on seek, correcting some issues with frame timing --- .../scripting/RecordingScriptingInterface.cpp | 28 +++++++++++------ .../scripting/RecordingScriptingInterface.h | 23 ++++++++------ libraries/recording/src/recording/Deck.cpp | 31 ++++++++++++++++--- 3 files changed, 59 insertions(+), 23 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index 4ca3881e8d..d549de84b2 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -47,19 +47,19 @@ RecordingScriptingInterface::RecordingScriptingInterface() { connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); } -bool RecordingScriptingInterface::isPlaying() { +bool RecordingScriptingInterface::isPlaying() const { return _player->isPlaying(); } -bool RecordingScriptingInterface::isPaused() { +bool RecordingScriptingInterface::isPaused() const { return _player->isPaused(); } -float RecordingScriptingInterface::playerElapsed() { +float RecordingScriptingInterface::playerElapsed() const { return _player->position(); } -float RecordingScriptingInterface::playerLength() { +float RecordingScriptingInterface::playerLength() const { return _player->length(); } @@ -103,6 +103,10 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) { } void RecordingScriptingInterface::setPlayerTime(float time) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setPlayerTime", Qt::BlockingQueuedConnection, Q_ARG(float, time)); + return; + } _player->seek(time); } @@ -130,23 +134,27 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode _useSkeletonModel = useSkeletonModel; } -void RecordingScriptingInterface::play() { - _player->play(); -} - void RecordingScriptingInterface::pausePlayer() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection); + return; + } _player->pause(); } void RecordingScriptingInterface::stopPlaying() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection); + return; + } _player->stop(); } -bool RecordingScriptingInterface::isRecording() { +bool RecordingScriptingInterface::isRecording() const { return _recorder->isRecording(); } -float RecordingScriptingInterface::recorderElapsed() { +float RecordingScriptingInterface::recorderElapsed() const { return _recorder->position(); } diff --git a/interface/src/scripting/RecordingScriptingInterface.h b/interface/src/scripting/RecordingScriptingInterface.h index 510a4b6898..27193e6f9a 100644 --- a/interface/src/scripting/RecordingScriptingInterface.h +++ b/interface/src/scripting/RecordingScriptingInterface.h @@ -25,12 +25,17 @@ public: RecordingScriptingInterface(); public slots: - bool isPlaying(); - bool isPaused(); - float playerElapsed(); - float playerLength(); void loadRecording(const QString& filename); + void startPlaying(); + void pausePlayer(); + void stopPlaying(); + bool isPlaying() const; + bool isPaused() const; + + float playerElapsed() const; + float playerLength() const; + void setPlayerVolume(float volume); void setPlayerAudioOffset(float audioOffset); void setPlayerTime(float time); @@ -40,13 +45,13 @@ public slots: void setPlayerUseAttachments(bool useAttachments); void setPlayerUseHeadModel(bool useHeadModel); void setPlayerUseSkeletonModel(bool useSkeletonModel); - void play(); - void pausePlayer(); - void stopPlaying(); - bool isRecording(); - float recorderElapsed(); + void startRecording(); void stopRecording(); + bool isRecording() const; + + float recorderElapsed() const; + void saveRecording(const QString& filename); void loadLastRecording(); diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index e52fcc16e6..6f624db191 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -8,6 +8,8 @@ #include "Deck.h" +#include + #include #include @@ -101,9 +103,13 @@ float Deck::position() const { } static const Frame::Time MIN_FRAME_WAIT_INTERVAL = Frame::secondsToFrameTime(0.001f); -static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.002f); +static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.004f); void Deck::processFrames() { + if (qApp->thread() != QThread::currentThread()) { + qWarning() << "Processing frames must only happen on the main thread."; + return; + } Locker lock(_mutex); if (_pause) { return; @@ -115,10 +121,17 @@ void Deck::processFrames() { // FIXME add code to start dropping frames if we fall behind. // Alternatively, add code to cache frames here and then process only the last frame of a given type // ... the latter will work for Avatar, but not well for audio I suspect. + bool overLimit = false; for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) { auto currentPosition = Frame::frameTimeFromEpoch(_startEpoch); if ((currentPosition - startingPosition) >= MAX_FRAME_PROCESSING_TIME) { qCWarning(recordingLog) << "Exceeded maximum frame processing time, breaking early"; +#ifdef WANT_RECORDING_DEBUG + qCDebug(recordingLog) << "Starting: " << currentPosition; + qCDebug(recordingLog) << "Current: " << startingPosition; + qCDebug(recordingLog) << "Trigger: " << triggerPosition; +#endif + overLimit = true; break; } @@ -150,9 +163,19 @@ void Deck::processFrames() { // If we have more clip frames available, set the timer for the next one _position = Frame::frameTimeFromEpoch(_startEpoch); - auto nextFrameTime = nextClip->positionFrameTime(); - auto interval = Frame::frameTimeToMilliseconds(nextFrameTime - _position); - _timer.singleShot(interval, [this] { + int nextInterval = 1; + if (!overLimit) { + auto nextFrameTime = nextClip->positionFrameTime(); + nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position); +#ifdef WANT_RECORDING_DEBUG + qCDebug(recordingLog) << "Now " << _position; + qCDebug(recordingLog) << "Next frame time " << nextInterval; +#endif + } +#ifdef WANT_RECORDING_DEBUG + qCDebug(recordingLog) << "Setting timer for next processing " << nextInterval; +#endif + _timer.singleShot(nextInterval, [this] { processFrames(); }); } From 0ad1d080411fb0f305ac098056c3eb25bd978d89 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 16:46:25 -0800 Subject: [PATCH 22/39] Exclude avatar scales out of the permissable range --- libraries/avatars/src/AvatarData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 017ef7578a..9f3cbf092b 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -178,7 +178,7 @@ float AvatarData::getTargetScale() const { void AvatarData::setTargetScale(float targetScale, bool overideReferential) { if (!_referential || overideReferential) { - _targetScale = targetScale; + _targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, targetScale)); } } @@ -532,7 +532,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } return maxAvailableSize; } - _targetScale = scale; + _targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, scale)); } // 20 bytes { // Lookat Position From e0a9048287e371f4455ec11c5a0653a9bff95713 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 17 Nov 2015 16:59:14 -0800 Subject: [PATCH 23/39] new bubble model --- examples/toybox/bubblewand/wand.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toybox/bubblewand/wand.js b/examples/toybox/bubblewand/wand.js index 4bdc789612..8036d9ead6 100644 --- a/examples/toybox/bubblewand/wand.js +++ b/examples/toybox/bubblewand/wand.js @@ -16,7 +16,7 @@ Script.include("../../libraries/utils.js"); - var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx"; + var BUBBLE_MODEL = "http://hifi-content.s3.amazonaws.com/james/bubblewand/bubble.fbx"; var BUBBLE_INITIAL_DIMENSIONS = { x: 0.01, From 4e57c9114cc1cc4a09774e532743672a5560f54e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 17:13:53 -0800 Subject: [PATCH 24/39] Avatar has no dependency on audio or recording anymore --- libraries/avatars/CMakeLists.txt | 2 +- libraries/avatars/src/AvatarData.cpp | 1 - libraries/avatars/src/AvatarData.h | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index 6d4d9cc341..fc6d15cced 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME avatars) setup_hifi_library(Network Script) -link_hifi_libraries(audio shared networking recording) +link_hifi_libraries(shared networking) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index fdfc6c1893..cc4139184d 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include "AvatarLogging.h" diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e79c0be80a..846c314e4b 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -50,7 +50,6 @@ typedef unsigned long long quint64; #include #include #include -#include #include "AABox.h" #include "HandData.h" From 94f18672d42ac8db2e5617605ffd85d1e62d3432 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 17:18:16 -0800 Subject: [PATCH 25/39] equip from a distance uses a spring to pull the object to the hand before equipping it --- examples/controllers/handControllerGrab.js | 92 ++++++++++++++++++++-- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index efd288c751..dd3a9a4b96 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -55,6 +55,13 @@ var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed +// +// equip +// + +var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; +var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position + // // other constants // @@ -70,7 +77,7 @@ var ZERO_VEC = { var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; var MSEC_PER_SEC = 1000.0; -// these control how long an abandoned pointer line will hang around +// these control how long an abandoned pointer line or action will hang around var LIFETIME = 10; var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; @@ -113,6 +120,7 @@ var STATE_EQUIP = 12 var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down var STATE_CONTINUE_EQUIP = 14; var STATE_WAITING_FOR_BUMPER_RELEASE = 15; +var STATE_EQUIP_SPRING = 16; function stateToName(state) { @@ -149,6 +157,8 @@ function stateToName(state) { return "continue_equip"; case STATE_WAITING_FOR_BUMPER_RELEASE: return "waiting_for_bumper_release"; + case STATE_EQUIP_SPRING: + return "state_equip_spring"; } return "unknown"; @@ -228,14 +238,15 @@ function MyController(hand) { this.continueDistanceHolding(); break; case STATE_NEAR_GRABBING: - this.nearGrabbing(); - break; case STATE_EQUIP: this.nearGrabbing(); break; case STATE_WAITING_FOR_BUMPER_RELEASE: this.waitingForBumperRelease(); break; + case STATE_EQUIP_SPRING: + this.pullTowardEquipPosition() + break; case STATE_CONTINUE_NEAR_GRABBING: case STATE_CONTINUE_EQUIP_BD: case STATE_CONTINUE_EQUIP: @@ -444,7 +455,15 @@ function MyController(hand) { return; } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // equipping + if (typeof grabbableData.spatialKey !== 'undefined') { + this.setState(STATE_EQUIP_SPRING); + } else { + this.setState(STATE_EQUIP); + } + } return; } } else if (! entityIsGrabbedByOther(intersection.entityID)) { @@ -455,7 +474,7 @@ function MyController(hand) { this.grabbedEntity = intersection.entityID; if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { // if a distance pick in equip mode hits something with a spatialKey, equip it - this.setState(STATE_EQUIP); + this.setState(STATE_EQUIP_SPRING); return; } else if (this.state == STATE_SEARCHING) { this.setState(STATE_DISTANCE_HOLDING); @@ -750,7 +769,13 @@ function MyController(hand) { this.actionID = null; } else { this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - this.setState(this.state == STATE_NEAR_GRABBING ? STATE_CONTINUE_NEAR_GRABBING : STATE_CONTINUE_EQUIP_BD) + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + this.setState(STATE_CONTINUE_EQUIP_BD); + } + if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { @@ -822,7 +847,60 @@ function MyController(hand) { this.setState(STATE_RELEASE); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); } - } + }; + + this.pullTowardEquipPosition = function() { + this.lineOff(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + // use a spring to pull the object to where it will be when equipped + var relativeRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }; + var relativePosition = { x: 0.0, y: 0.0, z: 0.0 }; + if (grabbableData.spatialKey.relativePosition) { + relativePosition = grabbableData.spatialKey.relativePosition; + } + if (grabbableData.spatialKey.relativeRotation) { + relativeRotation = grabbableData.spatialKey.relativeRotation; + } + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + var targetRotation = Quat.multiply(handRotation, relativeRotation); + var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); + var targetPosition = Vec3.sum(handPosition, offset); + + if (typeof this.equipSpringID === 'undefined' || + this.equipSpringID === null || + this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL + }); + if (this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = null; + this.setState(STATE_OFF); + return; + } + } else { + Entities.updateAction(this.grabbedEntity, this.equipSpringID, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL + }); + } + + if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { + Entities.deleteAction(this.grabbedEntity, this.equipSpringID); + this.equipSpringID = null; + this.setState(STATE_EQUIP); + } + }; this.nearTrigger = function() { if (this.triggerSmoothedReleased()) { From 8566d84709c7535472c4dda164a9e89a25fb4e7d Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Tue, 17 Nov 2015 10:51:34 -0800 Subject: [PATCH 26/39] fixed cleanup - unload --- examples/entityScripts/recordingEntityScript.js | 2 +- examples/entityScripts/recordingMaster.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/entityScripts/recordingEntityScript.js b/examples/entityScripts/recordingEntityScript.js index ede6f4fbe2..6f41d20e0e 100644 --- a/examples/entityScripts/recordingEntityScript.js +++ b/examples/entityScripts/recordingEntityScript.js @@ -80,7 +80,7 @@ } } }, - clean: function(entityID) { + unload: function(entityID) { Script.update.disconnect(_this.update); } } diff --git a/examples/entityScripts/recordingMaster.js b/examples/entityScripts/recordingMaster.js index 3cec521ce0..718d47eb92 100644 --- a/examples/entityScripts/recordingMaster.js +++ b/examples/entityScripts/recordingMaster.js @@ -106,7 +106,7 @@ function mousePressEvent(event) { function cleanup() { toolBar.cleanup(); - Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings + //Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings Entities.deleteEntity(recordAreaEntity); } From 5400381583df5061c9dc1334752ee8511be2d59d Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Tue, 17 Nov 2015 18:01:51 -0800 Subject: [PATCH 27/39] improved the structure --- examples/entityScripts/createRecorder.js | 21 +++++ .../entityScripts/recordingEntityScript.js | 81 +++++++++++-------- examples/entityScripts/recordingMaster.js | 54 +++++-------- 3 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 examples/entityScripts/createRecorder.js diff --git a/examples/entityScripts/createRecorder.js b/examples/entityScripts/createRecorder.js new file mode 100644 index 0000000000..7f89898ceb --- /dev/null +++ b/examples/entityScripts/createRecorder.js @@ -0,0 +1,21 @@ +var rotation = Quat.safeEulerAngles(Camera.getOrientation()); +rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(6, Quat.getFront(rotation))); + +var recordAreaEntity = Entities.addEntity({ + name: 'recorderEntity', + dimensions: { + x: 10, + y: 10, + z: 10 + }, + type: 'Box', + position: center, + color: { + red: 255, + green: 255, + blue: 255 + }, + visible: true, + script: "https://hifi-public.s3.amazonaws.com/sam/record/recordingEntityScript.js", +}); \ No newline at end of file diff --git a/examples/entityScripts/recordingEntityScript.js b/examples/entityScripts/recordingEntityScript.js index 6f41d20e0e..1b74466c4c 100644 --- a/examples/entityScripts/recordingEntityScript.js +++ b/examples/entityScripts/recordingEntityScript.js @@ -13,9 +13,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +(function () { + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js"); -(function() { var insideRecorderArea = false; var enteredInTime = false; var isAvatarRecording = false; @@ -25,51 +27,63 @@ _this = this; return; } - + + function update() { + var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted; + if (isRecordingStarted && !isAvatarRecording) { + _this.startRecording(); + } else if ((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)) { + _this.stopRecording(); + } else if (!isRecordingStarted && insideRecorderArea && !enteredInTime) { + //if an avatar enters the zone while a recording is started he will be able to participate to the next group recording + enteredInTime = true; + } + }; + recordingEntity.prototype = { - update: function(){ - var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); - var isRecordingStarted = userData.recordingKey.isRecordingStarted; - if(isRecordingStarted && !isAvatarRecording){ - _this.startRecording(); - }else if((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)){ - _this.stopRecording(); - }else if(!isRecordingStarted && insideRecorderArea && !enteredInTime){ - //if an avatar enters the zone while a recording is started he will be able to participate to the next group recording - enteredInTime = true; - } - - }, - preload: function(entityID) { + + preload: function (entityID) { + print("RECORDING ENTITY PRELOAD"); this.entityID = entityID; - Script.update.connect(_this.update); + + var entityProperties = Entities.getEntityProperties(_this.entityID); + if (!entityProperties.ignoreForCollisions) { + Entities.editEntity(_this.entityID, { ignoreForCollisions: true }); + } + + //print(JSON.stringify(entityProperties)); + var recordingKey = getEntityCustomData("recordingKey", _this.entityID, undefined); + if (recordingKey === undefined) { + setEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }); + } + + Script.update.connect(update); }, - enterEntity: function(entityID) { + enterEntity: function (entityID) { print("entering in the recording area"); insideRecorderArea = true; - var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); - var isRecordingStarted = userData.recordingKey.isRecordingStarted; - if(!isRecordingStarted){ + var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted; + if (!isRecordingStarted) { //i'm in the recording area in time (before the event starts) enteredInTime = true; } }, - leaveEntity: function(entityID) { + leaveEntity: function (entityID) { print("leaving the recording area"); insideRecorderArea = false; enteredInTime = false; }, - - startRecording: function(entityID){ - if(enteredInTime && !isAvatarRecording){ + + startRecording: function (entityID) { + if (enteredInTime && !isAvatarRecording) { print("RECORDING STARTED"); Recording.startRecording(); isAvatarRecording = true; } }, - - stopRecording: function(entityID){ - if(isAvatarRecording){ + + stopRecording: function (entityID) { + if (isAvatarRecording) { print("RECORDING ENDED"); Recording.stopRecording(); Recording.loadLastRecording(); @@ -80,12 +94,13 @@ } } }, - unload: function(entityID) { - Script.update.disconnect(_this.update); + unload: function (entityID) { + print("RECORDING ENTITY UNLOAD"); + Script.update.disconnect(update); } } - - + + return new recordingEntity(); -}); +}); \ No newline at end of file diff --git a/examples/entityScripts/recordingMaster.js b/examples/entityScripts/recordingMaster.js index 718d47eb92..71a92a05f3 100644 --- a/examples/entityScripts/recordingMaster.js +++ b/examples/entityScripts/recordingMaster.js @@ -5,16 +5,15 @@ // Created by Alessandro Signa on 11/12/15. // Copyright 2015 High Fidelity, Inc. // -// Run this script to spawn a box (recorder) and drive the start/end of the recording for anyone who is inside the box +// Run this script to find the recorder (created by crateRecorder.js) and drive the start/end of the recording for anyone who is inside the box // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -var PARAMS_SCRIPT_URL = Script.resolvePath('recordingEntityScript.js'); HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("../libraries/toolBars.js"); -Script.include("../libraries/utils.js"); +Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/toolBars.js"); +Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js"); @@ -30,35 +29,25 @@ var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; var toolBar = null; var recordIcon; - +var isRecordingEntityFound = false; var isRecording = false; -var recordAreaEntity = Entities.addEntity({ - name: 'recorderEntity', - dimensions: { - x: 2, - y: 1, - z: 2 - }, - type: 'Box', - position: center, - color: { - red: 255, - green: 255, - blue: 255 - }, - visible: true, - ignoreForCollisions: true, - script: PARAMS_SCRIPT_URL, - - userData: JSON.stringify({ - recordingKey: { - isRecordingStarted: false - } - }) -}); +var recordAreaEntity = null; +findRecorder(); +function findRecorder() { + foundEntities = Entities.findEntities(MyAvatar.position, 50); + for (var i = 0; i < foundEntities.length; i++) { + var name = Entities.getEntityProperties(foundEntities[i], "name").name; + if (name === "recorderEntity") { + recordAreaEntity = foundEntities[i]; + isRecordingEntityFound = true; + print("Found recorder Entity!"); + return; + } + } +} setupToolBar(); @@ -70,7 +59,7 @@ function setupToolBar() { Tool.IMAGE_HEIGHT /= 2; Tool.IMAGE_WIDTH /= 2; - toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); //put the button in the up-left corner + toolBar = new ToolBar(0, 100, ToolBar.HORIZONTAL); //put the button in the up-left corner toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); @@ -81,9 +70,8 @@ function setupToolBar() { width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON, - visible: true + visible: isRecordingEntityFound, }, true, isRecording); - } function mousePressEvent(event) { @@ -106,8 +94,6 @@ function mousePressEvent(event) { function cleanup() { toolBar.cleanup(); - //Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings - Entities.deleteEntity(recordAreaEntity); } From 68c6718d1ca93410594fb8ae5ffc7e1d6edfd7ad Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 18:20:55 -0800 Subject: [PATCH 28/39] Fixes for windows 64 bit builds --- cmake/externals/openvr/CMakeLists.txt | 11 ++++++++--- cmake/externals/sdl2/CMakeLists.txt | 11 +++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index dea59f41a0..f9d0ef5a71 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -25,9 +25,14 @@ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/headers CACHE TYPE INTERNA if (WIN32) - # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) + # FIXME need to account for different architectures + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win64/openvr_api.lib CACHE TYPE INTERNAL) + add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win64) + else() + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL) + add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) + endif() elseif(APPLE) diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index decd1c6906..ee1d57d2e9 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -66,8 +66,15 @@ if (APPLE) elseif (WIN32) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x64/SDL2.lib CACHE FILEPATH "Path to SDL2 library") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x64 CACHE PATH "Location of SDL2 DLL") + else() + set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") + endif() + else () ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") From c21fbc9a469ca0c6903b097f0fec53c7b94bd5d8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 18:53:23 -0800 Subject: [PATCH 29/39] fix for messages-mixer bail early on empty settings --- assignment-client/src/AssignmentClient.cpp | 12 +++--- assignment-client/src/AssignmentClient.h | 1 + .../src/messages/MessagesMixer.cpp | 43 ++++++++++--------- assignment-client/src/octree/OctreeServer.cpp | 11 +++-- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index bf5f9c3b7f..2d11f4d289 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -198,7 +198,7 @@ void AssignmentClient::sendStatusPacketToACM() { } void AssignmentClient::sendAssignmentRequest() { - if (!_currentAssignment) { + if (!_currentAssignment && !_isAssigned) { auto nodeList = DependencyManager::get(); @@ -229,8 +229,9 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer pac // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(*packet); - if (_currentAssignment) { + if (_currentAssignment && !_isAssigned) { qDebug() << "Received an assignment -" << *_currentAssignment; + _isAssigned = true; auto nodeList = DependencyManager::get(); @@ -309,12 +310,11 @@ void AssignmentClient::handleAuthenticationRequest() { } void AssignmentClient::assignmentCompleted() { - // we expect that to be here the previous assignment has completely cleaned up assert(_currentAssignment.isNull()); - // reset our current assignment pointer to NULL now that it has been deleted - _currentAssignment = NULL; + // reset our current assignment pointer to null now that it has been deleted + _currentAssignment = nullptr; // reset the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); @@ -330,4 +330,6 @@ void AssignmentClient::assignmentCompleted() { nodeList->setOwnerType(NodeType::Unassigned); nodeList->reset(); nodeList->resetNodeInterestSet(); + + _isAssigned = false; } diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 9d2c816861..9d7591f931 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -46,6 +46,7 @@ private: Assignment _requestAssignment; QPointer _currentAssignment; + bool _isAssigned { false }; QString _assignmentServerHostname; HifiSockAddr _assignmentServerSocket; QTimer _requestTimer; // timer for requesting and assignment diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 99798b2d4f..f91076c335 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -128,27 +128,30 @@ void MessagesMixer::run() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - + + // The messages-mixer currently does not have any settings, so it would be kind of insane to bail on an empty settings + // object. The below can be uncommented once messages-mixer settings are enabled. + // wait until we have the domain-server settings, otherwise we bail - DomainHandler& domainHandler = nodeList->getDomainHandler(); - - qDebug() << "Waiting for domain settings from domain-server."; - - // block until we get the settingsRequestComplete signal - QEventLoop loop; - connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); - connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); - domainHandler.requestDomainSettings(); - loop.exec(); - - if (domainHandler.getSettingsObject().isEmpty()) { - qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; - setFinished(true); - return; - } - - // parse the settings to pull out the values we need - parseDomainServerSettings(domainHandler.getSettingsObject()); +// DomainHandler& domainHandler = nodeList->getDomainHandler(); +// +// qDebug() << "Waiting for domain settings from domain-server."; +// +// // block until we get the settingsRequestComplete signal +// QEventLoop loop; +// connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); +// connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); +// domainHandler.requestDomainSettings(); +// loop.exec(); +// +// if (domainHandler.getSettingsObject().isEmpty()) { +// qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; +// setFinished(true); +// return; +// } +// +// // parse the settings to pull out the values we need +// parseDomainServerSettings(domainHandler.getSettingsObject()); } void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index ad3df11474..84749bd975 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -953,7 +953,6 @@ bool OctreeServer::readConfiguration() { if (domainHandler.getSettingsObject().isEmpty()) { qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; - setFinished(true); return false; } @@ -1086,12 +1085,16 @@ void OctreeServer::run() { auto nodeList = DependencyManager::get(); nodeList->setOwnerType(getMyNodeType()); - // use common init to setup common timers and logging commonInit(getMyLoggingServerTargetName(), getMyNodeType()); + + // we need to ask the DS about agents so we can ping/reply with them + nodeList->addNodeTypeToInterestSet(NodeType::Agent); // read the configuration from either the payload or the domain server configuration if (!readConfiguration()) { + qDebug() << "OctreeServer bailing on run since readConfiguration has failed."; + setFinished(true); return; // bailing on run, because readConfiguration failed } @@ -1100,10 +1103,6 @@ void OctreeServer::run() { connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - - // we need to ask the DS about agents so we can ping/reply with them - nodeList->addNodeTypeToInterestSet(NodeType::Agent); - #ifndef WIN32 setvbuf(stdout, NULL, _IOLBF, 0); #endif From 2355ba70caa81e460599651cfcc3116692d82e28 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 18:55:09 -0800 Subject: [PATCH 30/39] just remove messages-mixer settings grabbing all together --- .../src/messages/MessagesMixer.cpp | 30 ++----------------- .../src/messages/MessagesMixer.h | 2 -- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index f91076c335..d3662f3fb5 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -129,32 +129,6 @@ void MessagesMixer::run() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - // The messages-mixer currently does not have any settings, so it would be kind of insane to bail on an empty settings - // object. The below can be uncommented once messages-mixer settings are enabled. - - // wait until we have the domain-server settings, otherwise we bail -// DomainHandler& domainHandler = nodeList->getDomainHandler(); -// -// qDebug() << "Waiting for domain settings from domain-server."; -// -// // block until we get the settingsRequestComplete signal -// QEventLoop loop; -// connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); -// connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); -// domainHandler.requestDomainSettings(); -// loop.exec(); -// -// if (domainHandler.getSettingsObject().isEmpty()) { -// qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; -// setFinished(true); -// return; -// } -// -// // parse the settings to pull out the values we need -// parseDomainServerSettings(domainHandler.getSettingsObject()); -} - -void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { - // TODO - if we want options, parse them here... - const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; + // The messages-mixer currently does currently have any domain settings. If it did, they would be + // synchronously grabbed here. } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 12667bcc1b..65419a8ca6 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -35,8 +35,6 @@ private slots: void handleMessagesUnsubscribe(QSharedPointer packetList, SharedNodePointer senderNode); private: - void parseDomainServerSettings(const QJsonObject& domainSettings); - QHash> _channelSubscribers; }; From c1675bba2168b610e389fc7dc01409a01ba12001 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 17 Nov 2015 19:01:29 -0800 Subject: [PATCH 31/39] Fix typo introduced by debug --- examples/acScripts/ControlACs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index e3d63cc26e..ba066d9750 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -156,7 +156,7 @@ function sendCommand(id, action) { position: controlEntityPosition, dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, visible: false, - lifetime: 10000, + lifetime: 10, userData: JSON.stringify({ idKey: { uD_id: id From ec918a1cf5b16153eb1c5bf30c8cbcaf3a443861 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 19:10:16 -0800 Subject: [PATCH 32/39] Fix OpenSSL 64 bit search logic --- cmake/modules/FindOpenSSL.cmake | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake index db3b2ba477..2142322687 100644 --- a/cmake/modules/FindOpenSSL.cmake +++ b/cmake/modules/FindOpenSSL.cmake @@ -34,17 +34,26 @@ if (UNIX) endif () if (WIN32) - # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ) + file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL/" "C:/OpenSSL-Win32/" "C:/OpenSSL-Win64/" - ) + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + # http://www.slproweb.com/products/Win32OpenSSL.html + set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" + ) + set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win64" "C:/OpenSSL/" "C:/OpenSSL-Win64/") + else() + # http://www.slproweb.com/products/Win32OpenSSL.html + set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" + ) + set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "C:/OpenSSL/" "C:/OpenSSL-Win32/") + endif() + unset(_programfiles) set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS}) + else () include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") hifi_library_search_hints("openssl") From ee751ed1b9da0aefda68cb7018303d601b4896bc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 09:21:04 -0800 Subject: [PATCH 33/39] use kDop volume of avatar mesh parts for capsule --- interface/src/avatar/SkeletonModel.cpp | 16 ++---- libraries/fbx/src/FBXReader.cpp | 79 +++++++++++--------------- libraries/fbx/src/FBXReader.h | 1 - 3 files changed, 38 insertions(+), 58 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 87f0e631f2..c6c1ead8c1 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -625,19 +625,15 @@ void SkeletonModel::computeBoundingShape() { totalExtents.addPoint(glm::vec3(0.0f)); int numStates = _rig->getJointStateCount(); for (int i = 0; i < numStates; i++) { - const JointState& state = _rig->getJointState(i); - - const glm::mat4& jointTransform = state.getTransform(); - float scale = extractUniformScale(jointTransform); - // Each joint contributes a capsule defined by FBXJoint.shapeInfo. // For totalExtents we use the capsule endpoints expanded by the radius. + const JointState& state = _rig->getJointState(i); + const glm::mat4& jointTransform = state.getTransform(); const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo; - for (int j = 0; j < shapeInfo.points.size(); ++j) { - glm::vec3 transformedPoint = extractTranslation(jointTransform * glm::translate(shapeInfo.points[j])); - vec3 radius(scale * shapeInfo.radius); - totalExtents.addPoint(transformedPoint + radius); - totalExtents.addPoint(transformedPoint - radius); + if (shapeInfo.points.size() > 0) { + for (int j = 0; j < shapeInfo.points.size(); ++j) { + totalExtents.addPoint(extractTranslation(jointTransform * glm::translate(shapeInfo.points[j]))); + } } // HACK so that default legless robot doesn't knuckle-drag if (shapeInfo.points.size() == 0 && (state.getName() == "LeftFoot" || state.getName() == "RightFoot")) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index f9bb089a9c..3251b5b35d 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1460,7 +1460,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS it != extracted.newIndices.end() && it.key() == oldIndex; it++) { // remember vertices with at least 1/4 weight - const float EXPANSION_WEIGHT_THRESHOLD = 0.25f; + const float EXPANSION_WEIGHT_THRESHOLD = 0.99f; if (weight > EXPANSION_WEIGHT_THRESHOLD) { // transform to joint-frame and save for later const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(it.value())); @@ -1535,63 +1535,48 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS meshIDsToMeshIndices.insert(it.key(), meshIndex); } - // now that all joints have been scanned, compute a radius for each bone + ShapeVertices cardinalDirections; + cardinalDirections.push_back(Vectors::UNIT_X); + cardinalDirections.push_back(Vectors::UNIT_Y); + cardinalDirections.push_back(Vectors::UNIT_Z); + const float INV_SQRT_3 = 0.57735026918f; + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3)); + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3)); + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3)); + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)); + + // now that all joints have been scanned compute a k-Dop bounding volume of mesh glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); for (int i = 0; i < geometry.joints.size(); ++i) { FBXJoint& joint = geometry.joints[i]; // NOTE: points are in joint-frame - // compute average point ShapeVertices& points = shapeVertices[i]; - glm::vec3 avgPoint = glm::vec3(0.0f); - for (uint32_t j = 0; j < points.size(); ++j) { - avgPoint += points[j]; - } - avgPoint /= (float)points.size(); - - // compute axis from begin to avgPoint - glm::vec3 begin(0.0f); - glm::vec3 end = avgPoint; - glm::vec3 axis = end - begin; - float axisLength = glm::length(axis); - if (axisLength > EPSILON) { - axis /= axisLength; - } else { - axis = glm::vec3(0.0f); - } - - // measure average cylindrical radius - float avgRadius = 0.0f; if (points.size() > 0) { - float minProjection = FLT_MAX; - float maxProjection = -FLT_MIN; + // compute average point + glm::vec3 avgPoint = glm::vec3(0.0f); for (uint32_t j = 0; j < points.size(); ++j) { - glm::vec3 offset = points[j] - avgPoint; - float projection = glm::dot(offset, axis); - maxProjection = glm::max(maxProjection, projection); - minProjection = glm::min(minProjection, projection); - avgRadius += glm::length(offset - projection * axis); + avgPoint += points[j]; } - avgRadius /= (float)points.size(); - - // compute endpoints of capsule in joint-frame - glm::vec3 capsuleBegin = avgPoint; - glm::vec3 capsuleEnd = avgPoint; - if (maxProjection - minProjection < 2.0f * avgRadius) { - // the mesh-as-cylinder approximation is too short to collide as a capsule - // so we'll collapse it to a sphere (although that isn't a very good approximation) - capsuleBegin = avgPoint + 0.5f * (maxProjection + minProjection) * axis; - capsuleEnd = capsuleBegin; - } else { - capsuleBegin = avgPoint + (minProjection + avgRadius) * axis; - capsuleEnd = avgPoint + (maxProjection - avgRadius) * axis; + avgPoint /= (float)points.size(); + + // compute a k-Dop bounding volume + for (uint32_t j = 0; j < cardinalDirections.size(); ++j) { + float maxDot = -FLT_MAX; + float minDot = FLT_MIN; + for (uint32_t k = 0; k < points.size(); ++k) { + float kDot = glm::dot(cardinalDirections[j], points[k] - avgPoint); + if (kDot > maxDot) { + maxDot = kDot; + } + if (kDot < minDot) { + minDot = kDot; + } + } + joint.shapeInfo.points.push_back(avgPoint + maxDot * cardinalDirections[j]); + joint.shapeInfo.points.push_back(avgPoint + minDot * cardinalDirections[j]); } - - // save points for later - joint.shapeInfo.points.push_back(capsuleBegin); - joint.shapeInfo.points.push_back(capsuleEnd); } - joint.shapeInfo.radius = avgRadius; } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 8014718815..0ddbc11bda 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -56,7 +56,6 @@ public: struct FBXJointShapeInfo { // same units and frame as FBXJoint.translation QVector points; - float radius; }; /// A single joint (transformation node) extracted from an FBX document. From 0e3d3451d7d2356f6df6b2ee890e08d4ebdfc8dd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 18 Nov 2015 10:24:10 -0800 Subject: [PATCH 34/39] make keep alive pings the responsibility of NL --- interface/src/Application.cpp | 11 ----- interface/src/Application.h | 1 - interface/src/Menu.cpp | 1 - interface/src/Menu.h | 1 - interface/src/ui/Stats.cpp | 49 ++++++++------------ libraries/networking/src/LimitedNodeList.cpp | 16 ------- libraries/networking/src/LimitedNodeList.h | 1 - libraries/networking/src/NodeList.cpp | 16 +++++++ libraries/networking/src/NodeList.h | 4 ++ 9 files changed, 40 insertions(+), 60 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c77bb9a114..b1e5a676d6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -999,10 +999,6 @@ void Application::initializeGL() { connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); _entityEditSender.initialize(_enableProcessOctreeThread); - // call our timer function every second - connect(&pingTimer, &QTimer::timeout, this, &Application::ping); - pingTimer.start(1000); - _idleLoopStdev.reset(); // update before the first render @@ -2130,13 +2126,6 @@ bool Application::acceptSnapshot(const QString& urlString) { return true; } -// Every second, send a ping, if menu item is checked. -void Application::ping() { - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - DependencyManager::get()->sendPingPackets(); - } -} - void Application::idle(uint64_t now) { if (_aboutToQuit) { return; // bail early, nothing to do here. diff --git a/interface/src/Application.h b/interface/src/Application.h index 39e3879707..39f93f4b72 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -306,7 +306,6 @@ public slots: private slots: void clearDomainOctreeDetails(); - void ping(); void idle(uint64_t now); void aboutToQuit(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 24033325f6..0f97f5a975 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -502,7 +502,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); - addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests())); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index dfa2cfa41b..868afb0de8 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -280,7 +280,6 @@ namespace MenuOption { const QString Stats = "Stats"; const QString StopAllScripts = "Stop All Scripts"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; - const QString TestPing = "Test Ping"; const QString ThirdPerson = "Third Person"; const QString ThreePointCalibration = "3 Point Calibration"; const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 1c0c03e16c..3127e00783 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -127,36 +127,27 @@ void Stats::updateStats(bool force) { STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); // Second column: ping - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); - SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); - SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer); - STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); - STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); - STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1); - - //// Now handle entity servers, since there could be more than one, we average their ping times - int totalPingOctree = 0; - int octreeServerCount = 0; - int pingOctreeMax = 0; - nodeList->eachNode([&](const SharedNodePointer& node) { - // TODO: this should also support entities - if (node->getType() == NodeType::EntityServer) { - totalPingOctree += node->getPingMs(); - octreeServerCount++; - if (pingOctreeMax < node->getPingMs()) { - pingOctreeMax = node->getPingMs(); - } - } - }); - - // update the entities ping with the average for all connected entity servers - STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1); - } else { - // -2 causes the QML to hide the ping column - STAT_UPDATE(audioPing, -2); - } + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); + SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); + SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer); + STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); + STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); + STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1); + //// Now handle entity servers, since there could be more than one, we average their ping times + int totalPingOctree = 0; + int octreeServerCount = 0; + int pingOctreeMax = 0; + nodeList->eachNode([&](const SharedNodePointer& node) { + // TODO: this should also support entities + if (node->getType() == NodeType::EntityServer) { + totalPingOctree += node->getPingMs(); + octreeServerCount++; + if (pingOctreeMax < node->getPingMs()) { + pingOctreeMax = node->getPingMs(); + } + } + }); // Third column, avatar stats MyAvatar* myAvatar = avatarManager->getMyAvatar(); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 4a7844ecc7..e717856ca2 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -700,22 +700,6 @@ void LimitedNodeList::sendSTUNRequest() { _nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); } -void LimitedNodeList::sendPingPackets() { - eachMatchingNode([](const SharedNodePointer& node)->bool { - switch (node->getType()) { - case NodeType::AvatarMixer: - case NodeType::AudioMixer: - case NodeType::EntityServer: - case NodeType::AssetServer: - return true; - default: - return false; - } - }, [&](const SharedNodePointer& node) { - sendPacket(constructPingPacket(), *node); - }); -} - void LimitedNodeList::processSTUNResponse(std::unique_ptr packet) { // check the cookie to make sure this is actually a STUN response // and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 26e648421a..8daeb3d256 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -228,7 +228,6 @@ public slots: void startSTUNPublicSocketUpdate(); virtual void sendSTUNRequest(); - void sendPingPackets(); bool killNodeWithUUID(const QUuid& nodeUUID); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e03ac47854..1e2485345f 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -32,6 +32,7 @@ #include "udt/PacketHeaders.h" #include "SharedUtil.h" +const int KEEPALIVE_PING_INTERVAL_MS = 1000; NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) : LimitedNodeList(socketListenPort, dtlsListenPort), @@ -87,6 +88,12 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // anytime we get a new node we will want to attempt to punch to it connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch); + + // setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect) + _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); + connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); + connect(&_domainHandler, SIGNAL(connectedToDomain()), &_keepAlivePingTimer, SLOT(start())); + connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // we definitely want STUN to update our public socket, so call the LNL to kick that off startSTUNPublicSocketUpdate(); @@ -632,3 +639,12 @@ void NodeList::activateSocketFromNodeCommunication(QSharedPointer pack flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket); } } + +void NodeList::sendKeepAlivePings() { + qDebug() << "Sending keepalive pings!"; + eachMatchingNode([this](const SharedNodePointer& node)->bool { + return _nodeTypesOfInterest.contains(node->getType()); + }, [&](const SharedNodePointer& node) { + sendPacket(constructPingPacket(), *node); + }); +} diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 5b9a4e5ae5..02f49d2918 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -85,6 +85,7 @@ public slots: void processPingReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processICEPingPacket(QSharedPointer packet); + signals: void limitOfSilentDomainCheckInsReached(); private slots: @@ -95,6 +96,8 @@ private slots: void handleNodePingTimeout(); void pingPunchForDomainServer(); + + void sendKeepAlivePings(); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); @@ -118,6 +121,7 @@ private: int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; bool _isShuttingDown { false }; + QTimer _keepAlivePingTimer; }; #endif // hifi_NodeList_h From ae88057ad9eca74be8a87664436a3003f313b010 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 10:24:10 -0800 Subject: [PATCH 35/39] remove dangling whitespace --- interface/src/avatar/SkeletonModel.cpp | 4 ++-- interface/src/avatar/SkeletonModel.h | 2 +- libraries/fbx/src/FBXReader.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c6c1ead8c1..99c2d0e041 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -664,7 +664,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha // draw a blue sphere at the capsule top point glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f); - deferredLighting->renderSolidSphereInstance(batch, + deferredLighting->renderSolidSphereInstance(batch, Transform().setTranslation(topPoint).postScale(_boundingCapsuleRadius), glm::vec4(0.6f, 0.6f, 0.8f, alpha)); @@ -672,7 +672,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f); glm::vec3 axis = topPoint - bottomPoint; - deferredLighting->renderSolidSphereInstance(batch, + deferredLighting->renderSolidSphereInstance(batch, Transform().setTranslation(bottomPoint).postScale(_boundingCapsuleRadius), glm::vec4(0.8f, 0.8f, 0.6f, alpha)); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index dc08168a8c..c1938097c3 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -82,7 +82,7 @@ public: bool getNeckPosition(glm::vec3& neckPosition) const; bool getLocalNeckPosition(glm::vec3& neckPosition) const; - + /// Returns the rotation of the neck joint's parent from default orientation /// \return whether or not the neck was found bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 3251b5b35d..a4482574e2 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1559,7 +1559,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS avgPoint += points[j]; } avgPoint /= (float)points.size(); - + // compute a k-Dop bounding volume for (uint32_t j = 0; j < cardinalDirections.size(); ++j) { float maxDot = -FLT_MAX; @@ -1568,7 +1568,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS float kDot = glm::dot(cardinalDirections[j], points[k] - avgPoint); if (kDot > maxDot) { maxDot = kDot; - } + } if (kDot < minDot) { minDot = kDot; } From d90c073102ee8a7d98f145827c3dc6b409ccaba5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 18 Nov 2015 10:25:18 -0800 Subject: [PATCH 36/39] leverage NL keep alive pings in Agent --- assignment-client/src/Agent.cpp | 27 --------------------------- assignment-client/src/Agent.h | 2 -- 2 files changed, 29 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 1f56118177..8cc706992a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -105,7 +105,6 @@ void Agent::handleAudioPacket(QSharedPointer packet) { } const QString AGENT_LOGGING_NAME = "agent"; -const int PING_INTERVAL = 1000; void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); @@ -127,10 +126,6 @@ void Agent::run() { << NodeType::MessagesMixer ); - _pingTimer = new QTimer(this); - connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests())); - _pingTimer->start(PING_INTERVAL); - // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { @@ -383,28 +378,6 @@ void Agent::aboutToFinish() { _scriptEngine->stop(); } - if (_pingTimer) { - _pingTimer->stop(); - delete _pingTimer; - } - // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); } - -void Agent::sendPingRequests() { - auto nodeList = DependencyManager::get(); - - nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool { - switch (node->getType()) { - case NodeType::AvatarMixer: - case NodeType::AudioMixer: - case NodeType::EntityServer: - return true; - default: - return false; - } - }, [nodeList](const SharedNodePointer& node) { - nodeList->sendPacket(nodeList->constructPingPacket(), *node); - }); -} diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index ab000015d5..fe1fffce5a 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -58,14 +58,12 @@ private slots: void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); - void sendPingRequests(); void processAgentAvatarAndAudio(float deltaTime); private: std::unique_ptr _scriptEngine; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; - QTimer* _pingTimer; MixedAudioStream _receivedAudioStream; float _lastReceivedAudioLoudness; From c54dffac1232aa5d4d8df61911d449a6aeceac31 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 18 Nov 2015 10:32:35 -0800 Subject: [PATCH 37/39] fix entity-server avg ping, remove debug --- interface/src/ui/Stats.cpp | 3 +++ libraries/networking/src/NodeList.cpp | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 3127e00783..c379f31aab 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -148,6 +148,9 @@ void Stats::updateStats(bool force) { } } }); + + // update the entities ping with the average for all connected entity servers + STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1); // Third column, avatar stats MyAvatar* myAvatar = avatarManager->getMyAvatar(); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 1e2485345f..925c64c77a 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -92,7 +92,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect) _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); - connect(&_domainHandler, SIGNAL(connectedToDomain()), &_keepAlivePingTimer, SLOT(start())); + connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start())); connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // we definitely want STUN to update our public socket, so call the LNL to kick that off @@ -641,7 +641,6 @@ void NodeList::activateSocketFromNodeCommunication(QSharedPointer pack } void NodeList::sendKeepAlivePings() { - qDebug() << "Sending keepalive pings!"; eachMatchingNode([this](const SharedNodePointer& node)->bool { return _nodeTypesOfInterest.contains(node->getType()); }, [&](const SharedNodePointer& node) { From 5c6df734c51a40f68cea2f8f51a7f19a4a062349 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 10:47:53 -0800 Subject: [PATCH 38/39] initialize vector of vec3's --- libraries/fbx/src/FBXReader.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index a4482574e2..2d2b2c4b0a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1535,15 +1535,16 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS meshIDsToMeshIndices.insert(it.key(), meshIndex); } - ShapeVertices cardinalDirections; - cardinalDirections.push_back(Vectors::UNIT_X); - cardinalDirections.push_back(Vectors::UNIT_Y); - cardinalDirections.push_back(Vectors::UNIT_Z); const float INV_SQRT_3 = 0.57735026918f; - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3)); - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3)); - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3)); - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)); + ShapeVertices cardinalDirections = { + Vectors::UNIT_X, + Vectors::UNIT_Y, + Vectors::UNIT_Z, + glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) + }; // now that all joints have been scanned compute a k-Dop bounding volume of mesh glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); From 73055a59a78b1aa45dafc651275c5bdd601dd8d5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 14:48:59 -0800 Subject: [PATCH 39/39] fix crash on login for bad boolean logic --- libraries/animation/src/AvatarRig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 4dbf5207b1..b72e15a0ce 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -13,7 +13,7 @@ /// Updates the state of the joint at the specified index. void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) { - if (index < 0 && index >= _jointStates.size()) { + if (index < 0 || index >= _jointStates.size()) { return; // bail } JointState& state = _jointStates[index];