From 394985ef4538b8c3c3f968cb4bc66cde02035eea Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 14 May 2014 11:52:01 -0700 Subject: [PATCH 01/22] Switched to Halo gun in gun.js, work in progress on hydraMove to add flying --- examples/gun.js | 5 +-- examples/hydraMove.js | 98 ++++++++++++++++++++++++------------------- 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/examples/gun.js b/examples/gun.js index e404ae1d4d..a5c0ee83e0 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -39,7 +39,7 @@ var impactSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-pub var targetHitSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/hit.raw"); var targetLaunchSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/shoot.raw"); -var gunModel = "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/attachments/Raygun2.fst"; +var gunModel = "http://public.highfidelity.io/models/attachments/HaloGun.fst"; var audioOptions = new AudioInjectionOptions(); audioOptions.volume = 0.9; @@ -199,7 +199,7 @@ function playLoadSound() { Audio.playSound(loadSound, audioOptions); } -MyAvatar.attach(gunModel, "RightHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); +//MyAvatar.attach(gunModel, "RightHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); MyAvatar.attach(gunModel, "LeftHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); // Give a bit of time to load before playing sound @@ -320,7 +320,6 @@ function scriptEnding() { Overlays.deleteOverlay(reticle); Overlays.deleteOverlay(text); MyAvatar.detachOne(gunModel); - MyAvatar.detachOne(gunModel); } Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); diff --git a/examples/hydraMove.js b/examples/hydraMove.js index ad0eeddfca..638e5fe226 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -50,6 +50,13 @@ var LEFT_BUTTON_4 = 4; var RIGHT_PALM = 2; var RIGHT_BUTTON_4 = 10; + +function printVector(text, v, decimals) { + print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals)); +} + +var debug = true; + // Used by handleGrabBehavior() for managing the grab position changes function getAndResetGrabDelta() { var HAND_GRAB_SCALE_DISTANCE = 2.0; @@ -60,24 +67,12 @@ function getAndResetGrabDelta() { return result; } -// Used by handleGrabBehavior() for managing the grab velocity feature -function getAndResetGrabDeltaVelocity() { - var HAND_GRAB_SCALE_VELOCITY = 50.0; - var delta = Vec3.multiply(grabDeltaVelocity, (MyAvatar.scale * HAND_GRAB_SCALE_VELOCITY)); - grabDeltaVelocity = { x: 0, y: 0, z: 0}; - var avatarRotation = MyAvatar.orientation; - var result = Quat.multiply(avatarRotation, Vec3.multiply(delta, -1)); - return result; -} - -// Used by handleGrabBehavior() for managing the grab rotation feature -function getAndResetGrabRotation() { +function getGrabRotation() { var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation)); - grabStartRotation = grabCurrentRotation; return quatDiff; } -// handles all the grab related behavior: position (crawl), velocity (flick), and rotate (twist) +// When move button is pressed, process results function handleGrabBehavior(deltaTime) { // check for and handle grab behaviors grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4); @@ -88,9 +83,12 @@ function handleGrabBehavior(deltaTime) { if (grabbingWithRightHand && !wasGrabbingWithRightHand) { // Just starting grab, capture starting rotation grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); + grabStartPosition = Controller.getSpatialControlPosition(RIGHT_PALM); + if (debug) printVector("start position", grabStartPosition, 3); } if (grabbingWithRightHand) { - grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(RIGHT_PALM), deltaTime)); + //grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(RIGHT_PALM), deltaTime)); + grabDelta = Vec3.subtract(Controller.getSpatialControlPosition(RIGHT_PALM), grabStartPosition); grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); } if (!grabbingWithRightHand && wasGrabbingWithRightHand) { @@ -102,10 +100,13 @@ function handleGrabBehavior(deltaTime) { if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) { // Just starting grab, capture starting rotation grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); + grabStartPosition = Controller.getSpatialControlPosition(LEFT_PALM); + if (debug) printVector("start position", grabStartPosition, 3); } if (grabbingWithLeftHand) { - grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(LEFT_PALM), deltaTime)); + //grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(LEFT_PALM), deltaTime)); + grabDelta = Vec3.subtract(Controller.getSpatialControlPosition(LEFT_PALM), grabStartPosition); grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); } if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) { @@ -119,44 +120,53 @@ function handleGrabBehavior(deltaTime) { if (grabbing) { - // move position - var moveFromGrab = getAndResetGrabDelta(); - if (Vec3.length(moveFromGrab) > EPSILON) { - MyAvatar.position = Vec3.sum(MyAvatar.position, moveFromGrab); - velocity = { x: 0, y: 0, z: 0}; - } - - // add some rotation... - var deltaRotation = getAndResetGrabRotation(); - var GRAB_CONTROLLER_TURN_SCALING = 0.5; - var euler = Vec3.multiply(Quat.safeEulerAngles(deltaRotation), GRAB_CONTROLLER_TURN_SCALING); + var headOrientation = MyAvatar.headOrientation; + var front = Quat.getFront(headOrientation); + var right = Quat.getRight(headOrientation); + var up = Quat.getUp(headOrientation); - // Adjust body yaw by yaw from controller - var orientation = Quat.multiply(Quat.angleAxis(-euler.y, {x:0, y: 1, z:0}), MyAvatar.orientation); + //grabDelta = Quat.multiply(headOrientation, grabDelta); + //grabDelta = Quat.multiply(Camera.getOrientation(), grabDelta); + grabDelta = Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiply(grabDelta, -1)); + + if (debug) { + printVector("grabDelta: ", grabDelta, 3); + } + + var THRUST_GRAB_SCALING = 0.0; + + var thrustFront = Vec3.multiply(front, MyAvatar.scale * grabDelta.z * THRUST_GRAB_SCALING * deltaTime); + MyAvatar.addThrust(thrustFront); + var thrustRight = Vec3.multiply(right, MyAvatar.scale * grabDelta.x * THRUST_GRAB_SCALING * deltaTime); + MyAvatar.addThrust(thrustRight); + var thrustUp = Vec3.multiply(up, MyAvatar.scale * grabDelta.y * THRUST_GRAB_SCALING * deltaTime); + MyAvatar.addThrust(thrustUp); + + + // add some rotation... + var deltaRotation = getGrabRotation(); + var GRAB_CONTROLLER_PITCH_SCALING = 2.5; + var GRAB_CONTROLLER_YAW_SCALING = 2.5; + var GRAB_CONTROLLER_ROLL_SCALING = 2.5; + var euler = Quat.safeEulerAngles(deltaRotation); + + // Adjust body yaw by roll from controller + var orientation = Quat.multiply(Quat.angleAxis(((euler.y * GRAB_CONTROLLER_YAW_SCALING) + + (euler.z * GRAB_CONTROLLER_ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation); MyAvatar.orientation = orientation; // Adjust head pitch from controller - MyAvatar.headPitch = MyAvatar.headPitch - euler.x; + MyAvatar.headPitch = MyAvatar.headPitch + (euler.x * GRAB_CONTROLLER_PITCH_SCALING * deltaTime); + + // Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster) + } - // add some velocity... - if (stoppedGrabbing) { - velocity = Vec3.sum(velocity, getAndResetGrabDeltaVelocity()); - } - - // handle residual velocity - if(Vec3.length(velocity) > EPSILON) { - MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.multiply(velocity, deltaTime)); - // damp velocity - velocity = Vec3.multiply(velocity, damping); - } - - wasGrabbingWithRightHand = grabbingWithRightHand; wasGrabbingWithLeftHand = grabbingWithLeftHand; } -// Main update function that handles flying and grabbing behaviort +// Update for joysticks and move button function flyWithHydra(deltaTime) { var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER); From dbfa60ff1b7ffbd6c81ebaabc06be1651143d87d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 May 2014 14:46:48 -0700 Subject: [PATCH 02/22] Fix interface log file location Put in \High Fidelity\Interface\Logs rather than \interface\Logs. --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c23804166c..d20b46c9a9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -170,7 +170,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _packetsPerSecond(0), _bytesPerSecond(0), _previousScriptLocation(), - _logger(new FileLogger(this)), _runningScriptsWidget(new RunningScriptsWidget(_window)), _runningScriptsWidgetWasVisible(false) { @@ -190,6 +189,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); + _logger = new FileLogger(this); // After setting organization name in order to get correct directory + QSettings::setDefaultFormat(QSettings::IniFormat); _myAvatar = _avatarManager.getMyAvatar(); From 8388255542bf7a340930a64e5a29ed857a520730 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 14 May 2014 15:29:16 -0700 Subject: [PATCH 03/22] Working on alternate IK mode. --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/SkeletonModel.cpp | 9 ++++++++- interface/src/avatar/SkeletonModel.h | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2daf5b0240..7c8f1d75a9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -351,6 +351,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false); addDisabledActionAndSeparator(developerMenu, "Testing"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 230584bf07..2a521bf59c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -267,6 +267,7 @@ private: namespace MenuOption { const QString AboutApp = "About Interface"; const QString AlignForearmsWithWrists = "Align Forearms with Wrists"; + const QString AlternateIK = "Alternate IK"; const QString AmbientOcclusion = "Ambient Occlusion"; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index e48ebfa63c..1e57481c66 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -176,7 +176,10 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation; // set hand position, rotation - if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) { + setHandPosition(jointIndex, palm.getPosition(), palmRotation); + + } else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f); setJointPosition(parentJointIndex, palm.getPosition() + forearmVector * geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale)); @@ -276,3 +279,7 @@ void SkeletonModel::renderJointConstraints(int jointIndex) { glLineWidth(1.0f); } +void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation) { + +} + diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 20384829ea..77e6ea33d4 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -51,6 +51,7 @@ protected: private: void renderJointConstraints(int jointIndex); + void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); Avatar* _owningAvatar; }; From ca2d2c751ce08618095c17842ffce156e311a712 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 15 May 2014 00:34:09 -0700 Subject: [PATCH 04/22] fixing models not rendering sometimes --- interface/src/models/ModelTreeRenderer.cpp | 5 ++++- .../src/particles/ParticleTreeRenderer.cpp | 2 +- libraries/models/src/ModelTree.cpp | 8 +++++--- libraries/models/src/ModelTreeElement.cpp | 17 ++++++++++++----- libraries/models/src/ModelTreeElement.h | 17 +++++++++++++++-- libraries/octree/src/OctreeElement.cpp | 2 +- libraries/octree/src/OctreeElement.h | 4 ++++ libraries/octree/src/OctreeRenderer.cpp | 2 +- libraries/octree/src/OctreeRenderer.h | 5 ++++- 9 files changed, 47 insertions(+), 15 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index ae60683745..0f9da86887 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -74,6 +74,7 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { } void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { + args->_elementsTouched++; // actually render it here... // we need to iterate the actual modelItems of the element ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; @@ -165,7 +166,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) bool drawAsModel = modelItem.hasModel(); - args->_renderedItems++; + args->_itemsRendered++; if (drawAsModel) { glPushMatrix(); @@ -220,6 +221,8 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) glutSolidSphere(radius, 15, 15); glPopMatrix(); } + } else { + args->_itemsOutOfView++; } } } diff --git a/interface/src/particles/ParticleTreeRenderer.cpp b/interface/src/particles/ParticleTreeRenderer.cpp index 2983093564..38ef9c8516 100644 --- a/interface/src/particles/ParticleTreeRenderer.cpp +++ b/interface/src/particles/ParticleTreeRenderer.cpp @@ -76,7 +76,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg bool drawAsModel = particle.hasModel(); - args->_renderedItems++; + args->_itemsRendered++; if (drawAsModel) { glPushMatrix(); diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index 45694b081d..ee980b1185 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -491,11 +491,14 @@ void ModelTree::update() { lockForWrite(); _isDirty = true; - ModelTreeUpdateArgs args = { }; + ModelTreeUpdateArgs args; recurseTreeWithOperation(updateOperation, &args); // now add back any of the particles that moved elements.... int movingModels = args._movingModels.size(); + + qDebug() << "ModelTree::update()... movingModels=" << movingModels; + for (int i = 0; i < movingModels; i++) { bool shouldDie = args._movingModels[i].getShouldDie(); @@ -553,7 +556,7 @@ bool ModelTree::encodeModelsDeletedSince(quint64& sinceTime, unsigned char* outp memcpy(copyAt, &numberOfIds, sizeof(numberOfIds)); copyAt += sizeof(numberOfIds); outputLength += sizeof(numberOfIds); - + // we keep a multi map of model IDs to timestamps, we only want to include the model IDs that have been // deleted since we last sent to this node _recentlyDeletedModelsLock.lockForRead(); @@ -595,7 +598,6 @@ bool ModelTree::encodeModelsDeletedSince(quint64& sinceTime, unsigned char* outp // replace the correct count for ids included memcpy(numberOfIDsAt, &numberOfIds, sizeof(numberOfIds)); - return hasMoreToSend; } diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index c5dce04fe2..2f57818044 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -84,14 +84,17 @@ bool ModelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBit } bool ModelTreeElement::containsModelBounds(const ModelItem& model) const { - return _box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint()); + glm::vec3 clampedMin = glm::clamp(model.getMinimumPoint(), 0.0f, 1.0f); + glm::vec3 clampedMax = glm::clamp(model.getMaximumPoint(), 0.0f, 1.0f); + return _box.contains(clampedMin) && _box.contains(clampedMax); } bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { - if (_box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint())) { - int childForMinimumPoint = getMyChildContainingPoint(model.getMinimumPoint()); - int childForMaximumPoint = getMyChildContainingPoint(model.getMaximumPoint()); - + glm::vec3 clampedMin = glm::clamp(model.getMinimumPoint(), 0.0f, 1.0f); + glm::vec3 clampedMax = glm::clamp(model.getMaximumPoint(), 0.0f, 1.0f); + if (_box.contains(clampedMin) && _box.contains(clampedMax)) { + int childForMinimumPoint = getMyChildContainingPoint(clampedMin); + int childForMaximumPoint = getMyChildContainingPoint(clampedMax); // If I contain both the minimum and maximum point, but two different children of mine // contain those points, then I am the best fit for that model if (childForMinimumPoint != childForMaximumPoint) { @@ -102,10 +105,12 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { + args._totalElements++; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { ModelItem& model = (*modelItr); + args._totalItems++; // TODO: this _lastChanged isn't actually changing because we're not marking this element as changed. // how do we want to handle this??? We really only want to consider an element changed when it is @@ -119,6 +124,8 @@ void ModelTreeElement::update(ModelTreeUpdateArgs& args) { // erase this model modelItr = _modelItems->erase(modelItr); + + args._movingItems++; // this element has changed so mark it... markWithChangedTime(); diff --git a/libraries/models/src/ModelTreeElement.h b/libraries/models/src/ModelTreeElement.h index ce9e2dec7e..83b745206f 100644 --- a/libraries/models/src/ModelTreeElement.h +++ b/libraries/models/src/ModelTreeElement.h @@ -23,7 +23,16 @@ class ModelTreeElement; class ModelTreeUpdateArgs { public: + ModelTreeUpdateArgs() : + _totalElements(0), + _totalItems(0), + _movingItems(0) + { } + QList _movingModels; + int _totalElements; + int _totalItems; + int _movingItems; }; class FindAndUpdateModelItemIDArgs { @@ -63,7 +72,11 @@ public: /// Should this element be considered to have content in it. This will be used in collision and ray casting methods. /// By default we assume that only leaves are actual content, but some octrees may have different semantics. - virtual bool hasContent() const { return isLeaf(); } + virtual bool hasContent() const { return hasModels(); } + + /// Should this element be considered to have detailed content in it. Specifically should it be rendered. + /// By default we assume that only leaves have detailed content, but some octrees may have different semantics. + virtual bool hasDetailedContent() const { return hasModels(); } /// Override this to break up large octree elements when an edit operation is performed on a smaller octree element. /// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the @@ -92,7 +105,7 @@ public: const QList& getModels() const { return *_modelItems; } QList& getModels() { return *_modelItems; } - bool hasModels() const { return _modelItems->size() > 0; } + bool hasModels() const { return _modelItems ? _modelItems->size() > 0 : false; } void update(ModelTreeUpdateArgs& args); void setTree(ModelTree* tree) { _myTree = tree; } diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index edba26f2a7..0462a3b53d 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -1213,7 +1213,7 @@ bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float float furthestDistance = furthestDistanceToCamera(*viewFrustum); float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize); bool inChildBoundary = (furthestDistance <= childBoundary); - if (isLeaf() && inChildBoundary) { + if (hasDetailedContent() && inChildBoundary) { shouldRender = true; } else { float boundary = childBoundary * 2.0f; // the boundary is always twice the distance of the child boundary diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 42c9abad46..2485e49797 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -71,6 +71,10 @@ public: /// Should this element be considered to have content in it. This will be used in collision and ray casting methods. /// By default we assume that only leaves are actual content, but some octrees may have different semantics. virtual bool hasContent() const { return isLeaf(); } + + /// Should this element be considered to have detailed content in it. Specifically should it be rendered. + /// By default we assume that only leaves have detailed content, but some octrees may have different semantics. + virtual bool hasDetailedContent() const { return isLeaf(); } /// Override this to break up large octree elements when an edit operation is performed on a smaller octree element. /// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 1a85518181..e2aec8c890 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -156,7 +156,7 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) { } void OctreeRenderer::render(RenderMode renderMode) { - RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode }; + RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, 0, 0, 0 }; if (_tree) { _tree->lockForRead(); _tree->recurseTreeWithOperation(renderOperation, &args); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 73e26c97f6..18e68e26aa 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -71,12 +71,15 @@ protected: class RenderArgs { public: - int _renderedItems; OctreeRenderer* _renderer; ViewFrustum* _viewFrustum; float _sizeScale; int _boundaryLevelAdjust; OctreeRenderer::RenderMode _renderMode; + + int _elementsTouched; + int _itemsRendered; + int _itemsOutOfView; }; From 0db242bbaa222c7b8ff0e8937eb9de50b717bddf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 15 May 2014 00:46:09 -0700 Subject: [PATCH 05/22] cleanup debug --- libraries/models/src/ModelTree.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index ee980b1185..4e92544f40 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -497,8 +497,6 @@ void ModelTree::update() { // now add back any of the particles that moved elements.... int movingModels = args._movingModels.size(); - qDebug() << "ModelTree::update()... movingModels=" << movingModels; - for (int i = 0; i < movingModels; i++) { bool shouldDie = args._movingModels[i].getShouldDie(); From 7bee0478be2f53fdb8016f5576c0a45fa3d458e7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 15 May 2014 11:30:09 -0700 Subject: [PATCH 06/22] More work on integrating IK solution from Sixense. --- interface/src/avatar/SkeletonModel.cpp | 52 +++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1e57481c66..1921094a0f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -164,7 +164,8 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { // rotate palm to align with its normal (normal points out of hand's palm) glm::quat palmRotation; - if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { + if (!Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK) && + Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { getJointRotation(parentJointIndex, palmRotation, true); } else { getJointRotation(jointIndex, palmRotation, true); @@ -280,6 +281,55 @@ void SkeletonModel::renderJointConstraints(int jointIndex) { } void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation) { + // this algorithm is from sample code from sixense + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + int elbowJointIndex = geometry.joints.at(jointIndex).parentIndex; + if (elbowJointIndex == -1) { + return; + } + int shoulderJointIndex = geometry.joints.at(elbowJointIndex).parentIndex; + glm::vec3 shoulderPosition; + if (!getJointPosition(shoulderJointIndex, shoulderPosition)) { + return; + } + // precomputed lengths + float scale = extractUniformScale(_scale); + float upperArmLength = geometry.joints.at(elbowJointIndex).distanceToParent * scale; + float lowerArmLength = geometry.joints.at(jointIndex).distanceToParent * scale; + // first set wrist position + glm::vec3 wristPosition = position; + + glm::vec3 shoulderToWrist = wristPosition - shoulderPosition; + float distanceToWrist = glm::length(shoulderToWrist); + + // prevent gimbal lock + if (distanceToWrist > upperArmLength + lowerArmLength - EPSILON) { + distanceToWrist = upperArmLength + lowerArmLength - EPSILON; + shoulderToWrist = glm::normalize(shoulderToWrist) * distanceToWrist; + wristPosition = shoulderPosition + shoulderToWrist; + } + + // cosine of angle from upper arm to hand vector + float cosA = (upperArmLength * upperArmLength + distanceToWrist * distanceToWrist - lowerArmLength * lowerArmLength) / + (2 * upperArmLength * distanceToWrist); + float mid = upperArmLength * cosA; + float height = sqrt(upperArmLength * upperArmLength + mid * mid - 2 * upperArmLength * mid * cosA); + + // direction of the elbow + glm::vec3 handNormal = glm::cross(rotation * glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow rotating with wrist + glm::vec3 relaxedNormal = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow pointing straight down + const float NORMAL_WEIGHT = 0.5f; + glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT); + + if((jointIndex == geometry.rightHandJointIndex && finalNormal.y > 0.0f) || + (jointIndex == geometry.leftHandJointIndex && finalNormal.y < 0)) { + finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis) + } + + glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal)); + + // ik solution + glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height; } From f28ba72f757396277da7569c3caf3b6a43931bbb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 15 May 2014 11:37:58 -0700 Subject: [PATCH 07/22] When PrioVR is connected but head rotation is unavailable, use Faceshift rotation. --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/devices/PrioVR.cpp | 9 +++++++-- interface/src/devices/PrioVR.h | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1eac264ae4..5a58f18a97 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -237,7 +237,7 @@ void MyAvatar::simulate(float deltaTime) { void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; - if (Application::getInstance()->getPrioVR()->isActive()) { + if (Application::getInstance()->getPrioVR()->hasHeadRotation()) { estimatedRotation = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getHeadRotation())); estimatedRotation.x *= -1.0f; estimatedRotation.z *= -1.0f; diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 064e2be4b5..d8fda89e1a 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -62,8 +62,13 @@ PrioVR::~PrioVR() { #endif } -glm::quat PrioVR::getHeadRotation() const { - const int HEAD_ROTATION_INDEX = 0; +const int HEAD_ROTATION_INDEX = 0; + +bool PrioVR::hasHeadRotation() const { + return _humanIKJointIndices.size() > HEAD_ROTATION_INDEX && _humanIKJointIndices.at(HEAD_ROTATION_INDEX) != -1; +} + +glm::quat PrioVR::getHeadRotation() const { return _jointRotations.size() > HEAD_ROTATION_INDEX ? _jointRotations.at(HEAD_ROTATION_INDEX) : glm::quat(); } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 9cd7bda5d4..20f67df751 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -35,6 +35,8 @@ public: bool isActive() const { return !_jointRotations.isEmpty(); } + bool hasHeadRotation() const; + glm::quat getHeadRotation() const; glm::quat getTorsoRotation() const; From c12f436a7cf4e53906edcec9d009300fc982cf4a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 15 May 2014 11:47:11 -0700 Subject: [PATCH 08/22] Try smoothing the PrioVR input over a couple of frames. --- interface/src/devices/PrioVR.cpp | 7 ++++++- interface/src/devices/PrioVR.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index d8fda89e1a..ea6281fe4c 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -48,6 +48,7 @@ PrioVR::PrioVR() { return; } _jointRotations.resize(LIST_LENGTH); + _lastJointRotations.resize(LIST_LENGTH); for (int i = 0; i < LIST_LENGTH; i++) { _humanIKJointIndices.append(jointsDiscovered[i] ? indexOfHumanIKJoint(JOINT_NAMES[i]) : -1); } @@ -86,10 +87,14 @@ void PrioVR::update() { yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); - // convert to our expected coordinate system + // convert to our expected coordinate system, average with last rotations to smooth for (int i = 0; i < _jointRotations.size(); i++) { _jointRotations[i].y *= -1.0f; _jointRotations[i].z *= -1.0f; + + glm::quat lastRotation = _lastRotations.at(i); + _lastRotations[i] = _jointRotations.at(i); + _jointRotations[i] = safeMix(lastRotation, _jointRotations.at(i), 0.5f); } #endif } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 20f67df751..8f01574356 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -57,6 +57,7 @@ private: QVector _humanIKJointIndices; QVector _jointRotations; + QVector _lastJointRotations; QDateTime _calibrationCountdownStarted; }; From 8ee2642f0e23bf71e33e10357f318c01ca8e7bf6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 15 May 2014 12:10:59 -0700 Subject: [PATCH 09/22] Fixed compile error. --- interface/src/devices/PrioVR.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index ea6281fe4c..28564f6f2e 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -92,8 +92,8 @@ void PrioVR::update() { _jointRotations[i].y *= -1.0f; _jointRotations[i].z *= -1.0f; - glm::quat lastRotation = _lastRotations.at(i); - _lastRotations[i] = _jointRotations.at(i); + glm::quat lastRotation = _lastJointRotations.at(i); + _lastJointRotations[i] = _jointRotations.at(i); _jointRotations[i] = safeMix(lastRotation, _jointRotations.at(i), 0.5f); } #endif From 8e9defad3e05e09d5bcfe3a1c113948c2418360a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 15 May 2014 12:18:24 -0700 Subject: [PATCH 10/22] send an updated address to data-server with domain (when changed) --- interface/src/Application.cpp | 35 +++++++++++++++++++++++++++++ interface/src/Application.h | 1 + interface/src/avatar/MyAvatar.cpp | 30 ------------------------- interface/src/avatar/MyAvatar.h | 1 - libraries/shared/src/SharedUtil.cpp | 4 ++++ libraries/shared/src/SharedUtil.h | 2 ++ 6 files changed, 42 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ebcdcd878d..91e95bb4e3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -237,6 +237,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&nodeList->getDomainHandler(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); + + // update our location every 5 seconds in the data-server, assuming that we are authenticated with one + const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f; + + QTimer* locationUpdateTimer = new QTimer(this); + connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer); + locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled); @@ -3113,6 +3120,34 @@ void Application::updateWindowTitle(){ _window->setWindowTitle(title); } +void Application::updateLocationInServer() { + + AccountManager& accountManager = AccountManager::getInstance(); + + if (accountManager.isLoggedIn()) { + + static QJsonObject lastLocationObject; + + // construct a QJsonObject given the user's current address information + QJsonObject updatedLocationObject; + + QJsonObject addressObject; + addressObject.insert("position", QString(createByteArray(_myAvatar->getPosition()))); + addressObject.insert("orientation", QString(createByteArray(glm::degrees(safeEulerAngles(_myAvatar->getOrientation()))))); + addressObject.insert("domain", NodeList::getInstance()->getDomainHandler().getHostname()); + + updatedLocationObject.insert("address", addressObject); + + if (updatedLocationObject != lastLocationObject) { + + accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, + JSONCallbackParameters(), QJsonDocument(updatedLocationObject).toJson()); + + lastLocationObject = updatedLocationObject; + } + } +} + void Application::domainChanged(const QString& domainHostname) { updateWindowTitle(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 1b0c78e5fa..33ec9ca856 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -278,6 +278,7 @@ signals: public slots: void domainChanged(const QString& domainHostname); void updateWindowTitle(); + void updateLocationInServer(); void nodeAdded(SharedNodePointer node); void nodeKilled(SharedNodePointer node); void packetSent(quint64 length); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1eac264ae4..3fe48c868b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -49,8 +49,6 @@ const float COLLISION_RADIUS_SCALE = 0.125f; const float MIN_KEYBOARD_CONTROL_SPEED = 2.0f; const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED; -const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f; - // TODO: normalize avatar speed for standard avatar size, then scale all motion logic // to properly follow avatar size. float DEFAULT_MOTOR_TIMESCALE = 0.25f; @@ -83,11 +81,6 @@ MyAvatar::MyAvatar() : for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } - - // update our location every 5 seconds in the data-server, assuming that we are authenticated with one - QTimer* locationUpdateTimer = new QTimer(this); - connect(locationUpdateTimer, &QTimer::timeout, this, &MyAvatar::updateLocationInDataServer); - locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); } MyAvatar::~MyAvatar() { @@ -1434,29 +1427,6 @@ void MyAvatar::resetSize() { qDebug("Reseted scale to %f", _targetScale); } -static QByteArray createByteArray(const glm::vec3& vector) { - return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z); -} - -void MyAvatar::updateLocationInDataServer() { - // TODO: don't re-send this when it hasn't change or doesn't change by some threshold - // This will required storing the last sent values and clearing them when the AccountManager rootURL changes - - AccountManager& accountManager = AccountManager::getInstance(); - - if (accountManager.isLoggedIn()) { - QString positionString(createByteArray(_position)); - QString orientationString(createByteArray(glm::degrees(safeEulerAngles(getOrientation())))); - - // construct the json to put the user's location - QString locationPutJson = QString() + "{\"address\":{\"position\":\"" - + positionString + "\", \"orientation\":\"" + orientationString + "\"}}"; - - accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, - JSONCallbackParameters(), locationPutJson.toUtf8()); - } -} - void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) { if (jsonObject["status"].toString() == "success") { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2df74f23c2..2e75ac984d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -108,7 +108,6 @@ public slots: void decreaseSize(); void resetSize(); - void updateLocationInDataServer(); void goToLocationFromResponse(const QJsonObject& jsonObject); // Set/Get update the thrust that will move the avatar around diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 5cfd8961ec..1136d49dd4 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -828,3 +828,7 @@ bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, f float positionDistance = glm::distance(positionA, positionB); return (positionDistance <= similarEnough); } + +QByteArray createByteArray(const glm::vec3& vector) { + return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z); +} diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index d111439b7e..dbbfb02365 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -187,4 +187,6 @@ bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, f /// \return bool is the float NaN bool isNaN(float value); +QByteArray createByteArray(const glm::vec3& vector); + #endif // hifi_SharedUtil_h From 3eefb6a93ee73d45c1f0ed4cfa1dd2078fd54b9b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 15 May 2014 15:32:32 -0700 Subject: [PATCH 11/22] Rotation bits for alternate IK. --- interface/src/avatar/SkeletonModel.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1921094a0f..540565f5b8 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -322,8 +322,8 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c const float NORMAL_WEIGHT = 0.5f; glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT); - if((jointIndex == geometry.rightHandJointIndex && finalNormal.y > 0.0f) || - (jointIndex == geometry.leftHandJointIndex && finalNormal.y < 0)) { + bool rightHand = (jointIndex == geometry.rightHandJointIndex); + if (rightHand ? (finalNormal.y > 0.0f) : (finalNormal.y < 0.0f)) { finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis) } @@ -331,5 +331,17 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c // ik solution glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height; + + glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); + + glm::quat shoulderRotation; + getJointRotation(shoulderJointIndex, shoulderRotation, true); + applyRotationDelta(shoulderJointIndex, rotationBetween(shoulderRotation * forwardVector, elbowPosition - shoulderPosition), false); + + glm::quat elbowRotation; + getJointRotation(elbowJointIndex, elbowRotation, true); + applyRotationDelta(elbowJointIndex, rotationBetween(elbowRotation * forwardVector, wristPosition - elbowPosition), false); + + setJointRotation(jointIndex, rotation, true); } From 301234a39773208192066d0903ea4836fbf79bc4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 15 May 2014 15:34:01 -0700 Subject: [PATCH 12/22] Tabs -> spaces. --- interface/src/avatar/SkeletonModel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 540565f5b8..3786280a3c 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -320,16 +320,16 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c glm::vec3 handNormal = glm::cross(rotation * glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow rotating with wrist glm::vec3 relaxedNormal = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow pointing straight down const float NORMAL_WEIGHT = 0.5f; - glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT); + glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT); bool rightHand = (jointIndex == geometry.rightHandJointIndex); if (rightHand ? (finalNormal.y > 0.0f) : (finalNormal.y < 0.0f)) { finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis) } - glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal)); - - // ik solution + glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal)); + + // ik solution glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height; glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); From 61fd09e0bd16e985bfc8dfdd490d46ff5ff77b29 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 15 May 2014 16:24:31 -0700 Subject: [PATCH 13/22] Add ability to anchor an overlay --- examples/editModels.js | 39 +++++++++++++++----------- interface/src/ui/overlays/Overlay.cpp | 12 ++++++-- interface/src/ui/overlays/Overlay.h | 8 ++++++ interface/src/ui/overlays/Overlays.cpp | 12 ++++++++ 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 70a2e178ae..9a20a7db27 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -76,38 +76,42 @@ function controller(wichSide) { this.oldModelRadius; this.laser = Overlays.addOverlay("line3d", { - position: this.palmPosition, - end: this.tipPosition, + position: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, color: LASER_COLOR, alpha: 1, visible: false, - lineWidth: LASER_WIDTH + lineWidth: LASER_WIDTH, + anchor: "MyAvatar" }); this.guideScale = 0.02; this.ball = Overlays.addOverlay("sphere", { - position: this.palmPosition, + position: { x: 0, y: 0, z: 0 }, size: this.guideScale, solid: true, color: { red: 0, green: 255, blue: 0 }, alpha: 1, visible: false, + anchor: "MyAvatar" }); this.leftRight = Overlays.addOverlay("line3d", { - position: this.palmPosition, - end: this.tipPosition, + position: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, color: { red: 0, green: 0, blue: 255 }, alpha: 1, visible: false, - lineWidth: LASER_WIDTH + lineWidth: LASER_WIDTH, + anchor: "MyAvatar" }); this.topDown = Overlays.addOverlay("line3d", { - position: this.palmPosition, - end: this.tipPosition, + position: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, color: { red: 0, green: 0, blue: 255 }, alpha: 1, visible: false, - lineWidth: LASER_WIDTH + lineWidth: LASER_WIDTH, + anchor: "MyAvatar" }); @@ -170,10 +174,11 @@ function controller(wichSide) { } this.moveLaser = function () { - var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, LASER_LENGTH_FACTOR)); + var startPosition = Vec3.subtract(this.palmPosition, MyAvatar.position); + var endPosition = Vec3.sum(startPosition, Vec3.multiply(this.front, LASER_LENGTH_FACTOR)); Overlays.editOverlay(this.laser, { - position: this.palmPosition, + position: startPosition, end: endPosition, visible: true }); @@ -219,11 +224,11 @@ function controller(wichSide) { position: newPosition, modelRotation: newRotation }); - print("Moving " + this.modelID.id); +// print("Moving " + this.modelID.id); // Vec3.print("Old Position: ", this.oldModelPosition); // Vec3.print("Sav Position: ", newPosition); - Quat.print("Old Rotation: ", this.oldModelRotation); - Quat.print("New Rotation: ", newRotation); +// Quat.print("Old Rotation: ", this.oldModelRotation); +// Quat.print("New Rotation: ", newRotation); this.oldModelRotation = newRotation; this.oldModelPosition = newPosition; @@ -301,7 +306,7 @@ var rightController = new controller(RIGHT); function moveModels() { if (leftController.grabbing && rightController.grabbing && rightController.modelID.id == leftController.modelID.id) { - print("Both controllers"); + //print("Both controllers"); var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); @@ -319,7 +324,7 @@ function moveModels() { var newPosition = Vec3.sum(middle, Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); - Vec3.print("Ratio : " + ratio + " New position: ", newPosition); + //Vec3.print("Ratio : " + ratio + " New position: ", newPosition); var rotation = Quat.multiply(leftController.rotation, Quat.inverse(leftController.oldRotation)); rotation = Quat.multiply(rotation, leftController.oldModelRotation); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 3b38addb76..8ec7cbace1 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -23,7 +23,8 @@ Overlay::Overlay() : _parent(NULL), _alpha(DEFAULT_ALPHA), _color(DEFAULT_BACKGROUND_COLOR), - _visible(true) + _visible(true), + _anchor(NO_ANCHOR) { } @@ -51,8 +52,15 @@ void Overlay::setProperties(const QScriptValue& properties) { if (properties.property("alpha").isValid()) { setAlpha(properties.property("alpha").toVariant().toFloat()); } - + if (properties.property("visible").isValid()) { setVisible(properties.property("visible").toVariant().toBool()); } + + if (properties.property("anchor").isValid()) { + QString property = properties.property("anchor").toVariant().toString(); + if (property == "MyAvatar") { + setAnchor(MY_AVATAR); + } + } } diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 6feb159e05..7667b3d3fd 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -28,6 +28,11 @@ class Overlay : public QObject { Q_OBJECT public: + enum Anchor { + NO_ANCHOR, + MY_AVATAR + }; + Overlay(); ~Overlay(); void init(QGLWidget* parent); @@ -38,11 +43,13 @@ public: bool getVisible() const { return _visible; } const xColor& getColor() const { return _color; } float getAlpha() const { return _alpha; } + Anchor getAnchor() const { return _anchor; } // setters void setVisible(bool visible) { _visible = visible; } void setColor(const xColor& color) { _color = color; } void setAlpha(float alpha) { _alpha = alpha; } + void setAnchor(Anchor anchor) { _anchor = anchor; } virtual void setProperties(const QScriptValue& properties); @@ -51,6 +58,7 @@ protected: float _alpha; xColor _color; bool _visible; // should the overlay be drawn at all + Anchor _anchor; }; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 4eb4f030ac..78fe54b267 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -8,6 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "Cube3DOverlay.h" #include "ImageOverlay.h" @@ -57,8 +58,19 @@ void Overlays::render2D() { } void Overlays::render3D() { + glm::vec3 myAvatarPosition = Application::getInstance()->getAvatar()->getPosition(); + foreach(Overlay* thisOverlay, _overlays3D) { + glPushMatrix(); + switch (thisOverlay->getAnchor()) { + case Overlay::MY_AVATAR: + glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z); + break; + default: + break; + } thisOverlay->render(); + glPopMatrix(); } } From e8937852bc2661aef72c9a61d56e7f2bb77c99df Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Fri, 16 May 2014 15:12:49 +0200 Subject: [PATCH 14/22] MenuOption::Visage now default to false. --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2daf5b0240..3c0ebe4cd2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -331,7 +331,7 @@ Menu::Menu() : #endif #ifdef HAVE_VISAGE - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, true, + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, false, appInstance->getVisage(), SLOT(updateEnabled())); #endif From 51965fd283e43ea299d5d592175aec96eb7bb996 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Fri, 16 May 2014 15:44:07 +0200 Subject: [PATCH 15/22] changed float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal)); to float specular = max(0.0, dot(gl_LightSource[0].position, normalizedNormal)); Calculation for specular value has to be done like in all other shaders with the unmodified unnormalized lightsource position. Otherwise the specular effect will have weird behaviour. --- interface/resources/shaders/model.frag | 2 +- interface/resources/shaders/model_normal_map.frag | 2 +- interface/resources/shaders/model_normal_specular_map.frag | 2 +- interface/resources/shaders/model_specular_map.frag | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 29579d07ac..3964bd5b97 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -24,7 +24,7 @@ void main(void) { gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position))); // compute the specular component (sans exponent) - float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal)); + float specular = max(0.0, dot(gl_LightSource[0].position, normalizedNormal)); // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + diff --git a/interface/resources/shaders/model_normal_map.frag b/interface/resources/shaders/model_normal_map.frag index 2a4af2073a..a4f7a887c5 100644 --- a/interface/resources/shaders/model_normal_map.frag +++ b/interface/resources/shaders/model_normal_map.frag @@ -36,7 +36,7 @@ void main(void) { gl_FrontLightProduct[0].diffuse * max(0.0, dot(viewNormal, gl_LightSource[0].position))); // compute the specular component (sans exponent) - float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), viewNormal)); + float specular = max(0.0, dot(gl_LightSource[0].position, viewNormal)); // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + diff --git a/interface/resources/shaders/model_normal_specular_map.frag b/interface/resources/shaders/model_normal_specular_map.frag index 79761446b1..f5b9d2b06b 100644 --- a/interface/resources/shaders/model_normal_specular_map.frag +++ b/interface/resources/shaders/model_normal_specular_map.frag @@ -39,7 +39,7 @@ void main(void) { gl_FrontLightProduct[0].diffuse * max(0.0, dot(viewNormal, gl_LightSource[0].position))); // compute the specular component (sans exponent) - float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), viewNormal)); + float specular = max(0.0, dot(gl_LightSource[0].position, viewNormal)); // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) * diff --git a/interface/resources/shaders/model_specular_map.frag b/interface/resources/shaders/model_specular_map.frag index 972a8e2de6..4e2f3d0c98 100644 --- a/interface/resources/shaders/model_specular_map.frag +++ b/interface/resources/shaders/model_specular_map.frag @@ -27,7 +27,7 @@ void main(void) { gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position))); // compute the specular component (sans exponent) - float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal)); + float specular = max(0.0, dot(gl_LightSource[0].position, normalizedNormal)); // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) * From 85b94ff71cf6681a4704f85b10bdd723dfb9ec89 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 16 May 2014 09:20:15 -0700 Subject: [PATCH 16/22] added dead zone to pitch in hydra move, animated models show up where you are --- examples/animatedModelExample.js | 10 +++++----- examples/hydraMove.js | 24 ++++++++++++++++-------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js index 5199eb419f..3fb4ae8bd4 100644 --- a/examples/animatedModelExample.js +++ b/examples/animatedModelExample.js @@ -21,9 +21,9 @@ var roll = 0.0; var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll) var originalProperties = { - position: { x: 10, - y: 0, - z: 0 }, + position: { x: MyAvatar.position.x, + y: MyAvatar.position.y, + z: MyAvatar.position.z }, radius : 1, @@ -58,9 +58,9 @@ function moveModel(deltaTime) { if (animationFPS == 30) { animationFPS = 10; } else if (animationFPS == 10) { - animationFPS = 60; + animationFPS = 10; } else if (animationFPS == 60) { - animationFPS = 30; + animationFPS = 10; } print("animationFPS=" + animationFPS); isPlaying = true; diff --git a/examples/hydraMove.js b/examples/hydraMove.js index 638e5fe226..3a11fa0b5e 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -145,20 +145,28 @@ function handleGrabBehavior(deltaTime) { // add some rotation... var deltaRotation = getGrabRotation(); - var GRAB_CONTROLLER_PITCH_SCALING = 2.5; - var GRAB_CONTROLLER_YAW_SCALING = 2.5; - var GRAB_CONTROLLER_ROLL_SCALING = 2.5; + var PITCH_SCALING = 2.0; + var PITCH_DEAD_ZONE = 2.0; + var YAW_SCALING = 2.0; + var ROLL_SCALING = 2.0; + var euler = Quat.safeEulerAngles(deltaRotation); + print("dx: " + euler.x); + // Adjust body yaw by roll from controller - var orientation = Quat.multiply(Quat.angleAxis(((euler.y * GRAB_CONTROLLER_YAW_SCALING) + - (euler.z * GRAB_CONTROLLER_ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation); + var orientation = Quat.multiply(Quat.angleAxis(((euler.y * YAW_SCALING) + + (euler.z * ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation); MyAvatar.orientation = orientation; // Adjust head pitch from controller - MyAvatar.headPitch = MyAvatar.headPitch + (euler.x * GRAB_CONTROLLER_PITCH_SCALING * deltaTime); - - // Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster) + var pitch = 0.0; + if (Math.abs(euler.x) > PITCH_DEAD_ZONE) { + pitch = (euler.x < 0.0) ? (euler.x + PITCH_DEAD_ZONE) : (euler.x - PITCH_DEAD_ZONE); + } + MyAvatar.headPitch = MyAvatar.headPitch + (pitch * PITCH_SCALING * deltaTime); + + // TODO: Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster) } From 25f1e180f504c87c3a1ef704d30b24f8eb82fa51 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 16 May 2014 09:34:54 -0700 Subject: [PATCH 17/22] revert test change --- examples/animatedModelExample.js | 4 ++-- examples/bot.js | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js index 3fb4ae8bd4..70e40140eb 100644 --- a/examples/animatedModelExample.js +++ b/examples/animatedModelExample.js @@ -56,11 +56,11 @@ function moveModel(deltaTime) { if (count % adjustFPSEveryWhile == 0) { if (animationFPS == 30) { - animationFPS = 10; + animationFPS = 30; } else if (animationFPS == 10) { animationFPS = 10; } else if (animationFPS == 60) { - animationFPS = 10; + animationFPS = 60; } print("animationFPS=" + animationFPS); isPlaying = true; diff --git a/examples/bot.js b/examples/bot.js index f7a0429c53..e42d234abf 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -25,7 +25,7 @@ function printVector(string, vector) { } var CHANCE_OF_MOVING = 0.005; -var CHANCE_OF_SOUND = 0.000; +var CHANCE_OF_SOUND = 0.005; var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_BIG_MOVE = 0.1; var CHANCE_OF_WAVING = 0.009; @@ -41,11 +41,11 @@ var isWaving = false; var waveFrequency = 0.0; var waveAmplitude = 0.0; -var X_MIN = 20.0; -var X_MAX = 25.0; -var Z_MIN = 20.0; -var Z_MAX = 25.0; -var Y_PELVIS = 2.5; +var X_MIN = 5.0; +var X_MAX = 15.0; +var Z_MIN = 5.0; +var Z_MAX = 15.0; +var Y_PELVIS = 1.0; var SPINE_JOINT_NUMBER = 13; var SHOULDER_JOINT_NUMBER = 17; var ELBOW_JOINT_NUMBER = 18; From 4532c934199b7a0292c4a58ef18cb5dba952c60b Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 16 May 2014 09:36:55 -0700 Subject: [PATCH 18/22] debug off --- examples/hydraMove.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index 3a11fa0b5e..9465bd9447 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -55,7 +55,7 @@ function printVector(text, v, decimals) { print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals)); } -var debug = true; +var debug = false; // Used by handleGrabBehavior() for managing the grab position changes function getAndResetGrabDelta() { @@ -151,8 +151,6 @@ function handleGrabBehavior(deltaTime) { var ROLL_SCALING = 2.0; var euler = Quat.safeEulerAngles(deltaRotation); - - print("dx: " + euler.x); // Adjust body yaw by roll from controller var orientation = Quat.multiply(Quat.angleAxis(((euler.y * YAW_SCALING) + From 5dcd942e8e562872ed949d074951a7d7a890caa5 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 16 May 2014 09:44:03 -0700 Subject: [PATCH 19/22] removed dead code --- examples/hydraMove.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index 9465bd9447..ff71316886 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -87,7 +87,6 @@ function handleGrabBehavior(deltaTime) { if (debug) printVector("start position", grabStartPosition, 3); } if (grabbingWithRightHand) { - //grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(RIGHT_PALM), deltaTime)); grabDelta = Vec3.subtract(Controller.getSpatialControlPosition(RIGHT_PALM), grabStartPosition); grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); } @@ -105,7 +104,6 @@ function handleGrabBehavior(deltaTime) { } if (grabbingWithLeftHand) { - //grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(LEFT_PALM), deltaTime)); grabDelta = Vec3.subtract(Controller.getSpatialControlPosition(LEFT_PALM), grabStartPosition); grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); } @@ -125,9 +123,7 @@ function handleGrabBehavior(deltaTime) { var right = Quat.getRight(headOrientation); var up = Quat.getUp(headOrientation); - //grabDelta = Quat.multiply(headOrientation, grabDelta); - //grabDelta = Quat.multiply(Camera.getOrientation(), grabDelta); - grabDelta = Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiply(grabDelta, -1)); + grabDelta = Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiply(grabDelta, -1)); if (debug) { printVector("grabDelta: ", grabDelta, 3); From 59b23efbd84ccd926152a2e946f21a4f302aaa31 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 May 2014 10:37:28 -0700 Subject: [PATCH 20/22] Update old comment --- interface/src/ui/NodeBounds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/NodeBounds.cpp b/interface/src/ui/NodeBounds.cpp index 40ae606733..b46b729157 100644 --- a/interface/src/ui/NodeBounds.cpp +++ b/interface/src/ui/NodeBounds.cpp @@ -92,7 +92,7 @@ void NodeBounds::draw() { float scaleFactor = rootDetails.s * TREE_SCALE; - // Scale by 0.98 - 1.02 depending on the scale of the node. This allows smaller nodes to scale in + // Scale by 0.92 - 1.00 depending on the scale of the node. This allows smaller nodes to scale in // a bit and not overlap larger nodes. scaleFactor *= 0.92 + (rootDetails.s * 0.08); From 32974b40032a29320cb0290e5ff26684a9b1b070 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 May 2014 12:16:42 -0700 Subject: [PATCH 21/22] Remove QGLWidget include from NodeBounds --- interface/src/ui/NodeBounds.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/ui/NodeBounds.cpp b/interface/src/ui/NodeBounds.cpp index b46b729157..1f06f60c5c 100644 --- a/interface/src/ui/NodeBounds.cpp +++ b/interface/src/ui/NodeBounds.cpp @@ -12,8 +12,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - #include "Application.h" #include "NodeBounds.h" #include "Util.h" From 3f2d08871cef7ddf57fdab915b78a4604031a8b2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 May 2014 12:24:26 -0700 Subject: [PATCH 22/22] Reorder includes in NodeBounds.cpp --- interface/src/ui/NodeBounds.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/NodeBounds.cpp b/interface/src/ui/NodeBounds.cpp index 1f06f60c5c..6d4d809a86 100644 --- a/interface/src/ui/NodeBounds.cpp +++ b/interface/src/ui/NodeBounds.cpp @@ -13,9 +13,10 @@ // #include "Application.h" -#include "NodeBounds.h" #include "Util.h" +#include "NodeBounds.h" + NodeBounds::NodeBounds(QObject* parent) : QObject(parent), _showVoxelNodes(false),