diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 30c99f43f8..a8919105d1 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -176,6 +176,9 @@ function MyController(hand) { this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 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 }; var _this = this; @@ -291,7 +294,7 @@ function MyController(hand) { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.triggerSqueezed = function() { + this.triggerSqueezed = function() { var triggerValue = this.rawTriggerValue; return triggerValue > TRIGGER_ON_VALUE; }; @@ -352,7 +355,7 @@ function MyController(hand) { var intersection = Entities.findRayIntersection(pickRayBacked, true); - if (intersection.intersects && !intersection.properties.locked) { + if (intersection.intersects) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); this.grabbedEntity = intersection.entityID; @@ -369,7 +372,7 @@ function MyController(hand) { disabledHand = 'none'; } - if (!grabbableData.grabbable) { + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { this.grabbedEntity = null; continue; } @@ -379,10 +382,9 @@ function MyController(hand) { } if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } else { + } else if (!intersection.properties.locked) { this.setState(STATE_NEAR_GRABBING); } } else { @@ -391,7 +393,8 @@ function MyController(hand) { this.grabbedEntity = null; } else { // the hand is far from the intersected object. go into distance-holding mode - if (intersection.properties.collisionsWillMove) { + if (intersection.properties.collisionsWillMove + && !intersection.properties.locked) { this.setState(STATE_DISTANCE_HOLDING); } else { this.setState(STATE_FAR_GRABBING_NON_COLLIDING); @@ -503,7 +506,7 @@ function MyController(hand) { // How far did the avatar turn this timestep? // Note: The following code is too long because we need a Quat.quatBetween() function - // that returns the minimum quaternion between two quaternions. + // that returns the minimum quaternion between two quaternions. var currentOrientation = MyAvatar.orientation; if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { var negativeCurrentOrientation = { diff --git a/examples/tests/timerStressTest.js b/examples/tests/timerStressTest.js deleted file mode 100644 index a1cf76c5a6..0000000000 --- a/examples/tests/timerStressTest.js +++ /dev/null @@ -1,68 +0,0 @@ -// createBoxes.js -// part of bubblewand -// -// Created by James B. Pollack @imgntn -- 09/07/2015 -// Copyright 2015 High Fidelity, Inc. -// -// Loads a wand model and attaches the bubble wand behavior. -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - - - -Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js"); -Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js"); - -var bubbleModel = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx?' + randInt(0, 10000);; -//var scriptURL'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?'+randInt(0,10000); - -//for local testing -//var scriptURL = "http://localhost:8080/scripts/setRecurringTimeout.js?" + randInt(0, 10000); - - -var scriptURL='http://hifi-public.s3.amazonaws.com/james/debug/timeouts/setRecurringTimeout.js?'+ randInt(0, 10000); -//create the wand in front of the avatar - -var boxes=[]; -var TEST_ENTITY_NAME = "TimerScript"; - - -var TOTAL_ENTITIES = 100; -for (var i = 0; i < TOTAL_ENTITIES; i++) { - var box = Entities.addEntity({ - type: "Box", - name: TEST_ENTITY_NAME, - position: { - x: randInt(0, 100) - 50 + MyAvatar.position.x, - y: randInt(0, 100) - 50 + MyAvatar.position.x, - z: randInt(0, 100) - 50 + MyAvatar.position.x, - }, - dimensions: { - x: 1, - y: 1, - z: 1, - }, - color: { - red: 255, - green: 0, - blue: 0, - }, - //must be enabled to be grabbable in the physics engine - collisionsWillMove: true, - shapeType: 'box', - lifetime:60, - script: scriptURL - }); - boxes.push(box) -} - - -function cleanup() { - while (boxes.length > 0) { - Entities.deleteEntity(boxes.pop()); - - } -} - - -Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 6a3c562e44..c46bcd9402 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -1,15 +1,15 @@ { "name": "Vive to Standard", "channels": [ - { "from": "Vive.LY", "filters": [ "invert", { "type": "deadZone", "min": 0.7 } ], "to": "Standard.LY" }, - { "from": "Vive.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" }, + { "from": "Vive.LY", "when": "Vive.LS", "filters": "invert", "to": "Standard.LY" }, + { "from": "Vive.LX", "when": "Vive.LS", "to": "Standard.LX" }, { "from": "Vive.LT", "to": "Standard.LT" }, { "from": "Vive.LB", "to": "Standard.LB" }, { "from": "Vive.LS", "to": "Standard.LS" }, - { "from": "Vive.RY", "filters": "invert", "to": "Standard.RY" }, - { "from": "Vive.RX", "to": "Standard.RX" }, + { "from": "Vive.RY", "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" }, + { "from": "Vive.RX", "when": "Vive.RS", "to": "Standard.RX" }, { "from": "Vive.RT", "to": "Standard.RT" }, { "from": "Vive.RB", "to": "Standard.RB" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d41679bea..d31d9de4a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -159,8 +159,7 @@ static QTimer locationUpdateTimer; static QTimer balanceUpdateTimer; static QTimer identityPacketTimer; static QTimer billboardPacketTimer; -static QTimer checkFPStimer; -static QTimer idleTimer; +static QTimer pingTimer; static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SVO_EXTENSION = ".svo"; @@ -184,9 +183,7 @@ static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS static const QString INFO_HELP_PATH = "html/interface-welcome.html"; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; -static const unsigned int TARGET_SIM_FRAMERATE = 60; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; -static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; #ifndef __APPLE__ @@ -843,8 +840,7 @@ void Application::cleanupBeforeQuit() { balanceUpdateTimer.stop(); identityPacketTimer.stop(); billboardPacketTimer.stop(); - checkFPStimer.stop(); - idleTimer.stop(); + pingTimer.stop(); QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); // save state @@ -979,12 +975,9 @@ void Application::initializeGL() { _entityEditSender.initialize(_enableProcessOctreeThread); // call our timer function every second - connect(&checkFPStimer, &QTimer::timeout, this, &Application::checkFPS); - checkFPStimer.start(1000); + connect(&pingTimer, &QTimer::timeout, this, &Application::ping); + pingTimer.start(1000); - // call our idle function whenever we can - connect(&idleTimer, &QTimer::timeout, this, &Application::idle); - idleTimer.start(TARGET_SIM_FRAME_PERIOD_MS); _idleLoopStdev.reset(); // update before the first render @@ -1048,6 +1041,26 @@ void Application::initializeUi() { } void Application::paintGL() { + _frameCount++; + + // update fps moving average + uint64_t now = usecTimestampNow(); + static uint64_t lastPaintBegin{ now }; + uint64_t diff = now - lastPaintBegin; + if (diff != 0) { + _framesPerSecond.updateAverage((float)USECS_PER_SECOND / (float)diff); + } + + lastPaintBegin = now; + + // update fps once a second + if (now - _lastFramesPerSecondUpdate > USECS_PER_SECOND) { + _fps = _framesPerSecond.getAverage(); + _lastFramesPerSecondUpdate = now; + } + + idle(now); + PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("paintGL"); @@ -1314,7 +1327,6 @@ void Application::paintGL() { { PerformanceTimer perfTimer("makeCurrent"); _offscreenContext->makeCurrent(); - _frameCount++; Stats::getInstance()->setRenderDetails(renderArgs._details); // Reset the gpu::Context Stages @@ -2087,20 +2099,14 @@ bool Application::acceptSnapshot(const QString& urlString) { return true; } -// Every second, check the frame rates and other stuff -void Application::checkFPS() { +// Every second, send a ping, if menu item is checked. +void Application::ping() { if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { DependencyManager::get()->sendPingPackets(); } - - float diffTime = (float)_timerStart.nsecsElapsed() / 1000000000.0f; - - _fps = (float)_frameCount / diffTime; - _frameCount = 0; - _timerStart.start(); } -void Application::idle() { +void Application::idle(uint64_t now) { if (_aboutToQuit) { return; // bail early, nothing to do here. } @@ -2123,7 +2129,6 @@ void Application::idle() { { PROFILE_RANGE(__FUNCTION__); - uint64_t now = usecTimestampNow(); static uint64_t lastIdleStart{ now }; uint64_t idleStartToStartDuration = now - lastIdleStart; if (idleStartToStartDuration != 0) { diff --git a/interface/src/Application.h b/interface/src/Application.h index dfc904e0ef..212687c11e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -306,8 +306,8 @@ public slots: private slots: void clearDomainOctreeDetails(); - void checkFPS(); - void idle(); + void ping(); + void idle(uint64_t now); void aboutToQuit(); void handleScriptEngineLoaded(const QString& scriptFilename); @@ -541,6 +541,8 @@ private: EntityItemID _keyboardFocusedItem; quint64 _lastAcceptedKeyPress = 0; + SimpleMovingAverage _framesPerSecond{10}; + quint64 _lastFramesPerSecondUpdate = 0; SimpleMovingAverage _simsPerSecond{10}; int _simsPerSecondReport = 0; quint64 _lastSimsPerSecondUpdate = 0; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 675a71a2f4..b979334383 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -661,6 +661,9 @@ void Avatar::updateJointMappings() { } void Avatar::renderBillboard(RenderArgs* renderArgs) { + // FIXME disabling the billboard because it doesn't appear to work reliably + // the billboard is ending up with a random texture and position. + return; if (_billboard.isEmpty()) { return; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b0da8faeca..4b9bfd21a3 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -286,10 +286,10 @@ void AvatarManager::handleOutgoingChanges(const VectorOfMotionStates& motionStat void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents) { for (Collision collision : collisionEvents) { - // TODO: Current physics uses null idA or idB for non-entities. The plan is to handle MOTIONSTATE_TYPE_AVATAR, - // and then MOTIONSTATE_TYPE_MYAVATAR. As it is, this code only covers the case of my avatar (in which case one - // if the ids will be null), and the behavior for other avatars is not specified. This has to be fleshed - // out as soon as we use the new motionstates. + // TODO: The plan is to handle MOTIONSTATE_TYPE_AVATAR, and then MOTIONSTATE_TYPE_MYAVATAR. As it is, other + // people's avatars will have an id that doesn't match any entities, and one's own avatar will have + // an id of null. Thus this code handles any collision in which one of the participating objects is + // my avatar. (Other user machines will make a similar analysis and inject sound for their collisions.) if (collision.idA.isNull() || collision.idB.isNull()) { MyAvatar* myAvatar = getMyAvatar(); const QString& collisionSoundURL = myAvatar->getCollisionSoundURL(); @@ -299,9 +299,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION); if (!isSound) { - // TODO: When the new motion states are used, we'll probably break from the whole loop as soon as we hit our own avatar - // (regardless of isSound), because other users should inject for their own avatars. - continue; + return; // No sense iterating for others. We only have one avatar. } // Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for. const float energy = velocityChange * velocityChange; @@ -314,7 +312,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition()); myAvatar->collisionWithEntity(collision); - } + return; } } } } diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index cabe545f5a..acd9a45aab 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -18,6 +18,7 @@ AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); + _type = MOTIONSTATE_TYPE_AVATAR; if (_shape) { _mass = 100.0f; // HACK } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 00870c62c6..741a82fdb8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -121,6 +121,8 @@ MyAvatar::MyAvatar(RigPointer rig) : connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, &MyAvatar::goToLocation); _characterController.setEnabled(true); + + _bodySensorMatrix = deriveBodyFromHMDSensor(); } MyAvatar::~MyAvatar() { @@ -345,23 +347,6 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } void MyAvatar::updateHMDFollowVelocity() { - bool isMoving; - if (_lastIsMoving) { - const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec - isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; - } else { - const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec - isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; - } - - bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; - _lastIsMoving = isMoving; - - bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); - if (hmdIsAtRest || justStartedMoving) { - _isFollowingHMD = true; - } - // compute offset to body's target position (in sensor-frame) auto sensorBodyMatrix = deriveBodyFromHMDSensor(); _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); @@ -370,13 +355,29 @@ void MyAvatar::updateHMDFollowVelocity() { // don't pull the body DOWN to match the target (allow animation system to squat) truncatedOffset.y = 0.0f; } + float truncatedOffsetDistance = glm::length(truncatedOffset); + + bool isMoving; + if (_lastIsMoving) { + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; + } else { + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; + } + bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; + _lastIsMoving = isMoving; + bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); + const float MIN_HMD_HIP_SHIFT = 0.05f; + if (justStartedMoving || (hmdIsAtRest && truncatedOffsetDistance > MIN_HMD_HIP_SHIFT)) { + _isFollowingHMD = true; + } bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); if (!needNewFollowSpeed) { // check to see if offset has exceeded its threshold - float distance = glm::length(truncatedOffset); const float MAX_HMD_HIP_SHIFT = 0.2f; - if (distance > MAX_HMD_HIP_SHIFT) { + if (truncatedOffsetDistance > MAX_HMD_HIP_SHIFT) { _isFollowingHMD = true; needNewFollowSpeed = true; } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d764cd7bab..156f16cf46 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -152,7 +152,6 @@ public slots: Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords); signals: - void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void canAdjustLocksChanged(bool canAdjustLocks); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index f3ef855e50..665ae9706d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -360,16 +360,16 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { glm::vec3 velocityChange = (motionStateA ? motionStateA->getObjectLinearVelocityChange() : glm::vec3(0.0f)) + (motionStateB ? motionStateB->getObjectLinearVelocityChange() : glm::vec3(0.0f)); - if (motionStateA && motionStateA->getType() == MOTIONSTATE_TYPE_ENTITY) { + if (motionStateA) { QUuid idA = motionStateA->getObjectID(); QUuid idB; - if (motionStateB && motionStateB->getType() == MOTIONSTATE_TYPE_ENTITY) { + if (motionStateB) { idB = motionStateB->getObjectID(); } glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset; glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB); _collisionEvents.push_back(Collision(type, idA, idB, position, penetration, velocityChange)); - } else if (motionStateB && motionStateB->getType() == MOTIONSTATE_TYPE_ENTITY) { + } else if (motionStateB) { QUuid idB = motionStateB->getObjectID(); glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset; // NOTE: we're flipping the order of A and B (so that the first objectID is never NULL) diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 17a5afd09a..71b6cf3872 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -84,7 +84,7 @@ private: const glm::vec3& TWO() { return Vectors::TWO; } const glm::vec3& HALF() { return Vectors::HALF; } const glm::vec3& RIGHT() { return Vectors::RIGHT; } - const glm::vec3& UP() { return Vectors::UNIT_X; } + const glm::vec3& UP() { return Vectors::UP; } const glm::vec3& FRONT() { return Vectors::FRONT; } };