diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 34feafbd4d..5d36a6d261 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -170,9 +170,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); // Define the minimum bubble size - static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f); + static const glm::vec3 minBubbleSize = avatar.getSensorToWorldScale() * glm::vec3(0.3f, 1.3f, 0.3f); // Define the scale of the box for the current node - glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f; + glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f * avatar.getSensorToWorldScale(); // Set up the bounding box for the current node AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale); // Clamp the size of the bounding box to a minimum scale @@ -209,7 +209,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map return nodeData->getLastBroadcastTime(avatarNode->getUUID()); }, [&](AvatarSharedPointer avatar)->float{ - glm::vec3 nodeBoxHalfScale = (avatar->getPosition() - avatar->getGlobalBoundingBoxCorner()); + glm::vec3 nodeBoxHalfScale = (avatar->getPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale()); return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); }, [&](AvatarSharedPointer avatar)->bool { if (avatar == thisAvatar) { @@ -244,9 +244,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // Check to see if the space bubble is enabled // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { - + float sensorToWorldScale = avatarNodeData->getAvatarSharedPointer()->getSensorToWorldScale(); // Define the scale of the box for the current other node - glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f * sensorToWorldScale; // Set up the bounding box for the current other node AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); // Clamp the size of the bounding box to a minimum scale @@ -334,8 +334,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); + // determine if avatar is in view, to determine how much data to include... - glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); bool isInView = nodeData->otherAvatarInView(otherNodeBox); diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 106e067968..b9633104d5 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -486,9 +486,9 @@ ModalWindow { model: filesModel function updateSort() { - model.sortOrder = sortIndicatorOrder; - model.sortColumn = sortIndicatorColumn; - model.update(); + fileTableModel.sortOrder = sortIndicatorOrder; + fileTableModel.sortColumn = sortIndicatorColumn; + fileTableModel.update(); } onSortIndicatorColumnChanged: { updateSort(); } diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index 9e1d0a9f5a..d3b738469e 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -484,9 +484,9 @@ TabletModalWindow { model: filesModel function updateSort() { - model.sortOrder = sortIndicatorOrder; - model.sortColumn = sortIndicatorColumn; - model.update(); + fileTableModel.sortOrder = sortIndicatorOrder; + fileTableModel.sortColumn = sortIndicatorColumn; + fileTableModel.update(); } onSortIndicatorColumnChanged: { updateSort(); } diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 94847b2973..e3170f85ef 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -14,6 +14,8 @@ import "../../windows" as Windows import QtQuick 2.0 import Hifi 1.0 +import Qt.labs.settings 1.0 + Windows.ScrollingWindow { id: tabletRoot objectName: "tabletRoot" @@ -25,8 +27,32 @@ Windows.ScrollingWindow { shown: false resizable: false + Settings { + id: settings + category: "WindowRoot.Windows" + property real width: 480 + property real height: 706 + } + + onResizableChanged: { + if (!resizable) { + // restore default size + settings.width = tabletRoot.width + settings.height = tabletRoot.height + tabletRoot.width = 480 + tabletRoot.height = 706 + } else { + tabletRoot.width = settings.width + tabletRoot.height = settings.height + } + } + signal showDesktop(); + function setResizable(value) { + tabletRoot.resizable = value; + } + function setMenuProperties(rootMenu, subMenu) { tabletRoot.rootMenu = rootMenu; tabletRoot.subMenu = subMenu; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8a2cd5b4c5..5700bb6a72 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2413,10 +2413,18 @@ void Application::paintGL() { auto lodManager = DependencyManager::get(); RenderArgs renderArgs; + + float sensorToWorldScale = getMyAvatar()->getSensorToWorldScale(); { PROFILE_RANGE(render, "/buildFrustrumAndArgs"); { QMutexLocker viewLocker(&_viewMutex); + // adjust near clip plane to account for sensor scaling. + auto adjustedProjection = glm::perspective(_viewFrustum.getFieldOfView(), + _viewFrustum.getAspectRatio(), + DEFAULT_NEAR_CLIP * sensorToWorldScale, + _viewFrustum.getFarClip()); + _viewFrustum.setProjection(adjustedProjection); _viewFrustum.calculate(); } renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), @@ -2464,7 +2472,7 @@ void Application::paintGL() { PerformanceTimer perfTimer("CameraUpdates"); auto myAvatar = getMyAvatar(); - boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; + boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; // The render mode is default or mirror if the camera is in mirror mode, assigned further below renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; @@ -2476,7 +2484,7 @@ void Application::paintGL() { if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setOrientation(glm::quat_cast(camMat)); + _myCamera.setOrientation(glmExtractRotation(camMat)); } else { _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); @@ -2484,7 +2492,7 @@ void Application::paintGL() { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setOrientation(glm::normalize(glm::quat_cast(hmdWorldMat))); + _myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat))); _myCamera.setPosition(extractTranslation(hmdWorldMat) + myAvatar->getOrientation() * boomOffset); } else { @@ -2517,14 +2525,14 @@ void Application::paintGL() { hmdOffset.x = -hmdOffset.x; _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) + + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + mirrorBodyOrientation * hmdOffset); } else { _myCamera.setOrientation(myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) + + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } @@ -2552,7 +2560,7 @@ void Application::paintGL() { { PROFILE_RANGE(render, "/updateCompositor"); - getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); + getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform(), getMyAvatar()->getSensorToWorldMatrix()); } gpu::FramebufferPointer finalFramebuffer; @@ -2566,6 +2574,12 @@ void Application::paintGL() { finalFramebuffer = framebufferCache->getFramebuffer(); } + auto hmdInterface = DependencyManager::get(); + float ipdScale = hmdInterface->getIPDScale(); + + // scale IPD by sensorToWorldScale, to make the world seem larger or smaller accordingly. + ipdScale *= sensorToWorldScale; + mat4 eyeProjections[2]; { PROFILE_RANGE(render, "/mainRender"); @@ -2575,6 +2589,7 @@ void Application::paintGL() { // in the overlay render? // Viewport is assigned to the size of the framebuffer renderArgs._viewport = ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height()); + auto baseProjection = renderArgs.getViewFrustum().getProjection(); if (displayPlugin->isStereo()) { // Stereo modes will typically have a larger projection matrix overall, // so we ask for the 'mono' projection matrix, which for stereo and HMD @@ -2585,12 +2600,10 @@ void Application::paintGL() { // just relying on the left FOV in each case and hoping that the // overall culling margin of error doesn't cause popping in the // right eye. There are FIXMEs in the relevant plugins - _myCamera.setProjection(displayPlugin->getCullingProjection(_myCamera.getProjection())); + _myCamera.setProjection(displayPlugin->getCullingProjection(baseProjection)); renderArgs._context->enableStereo(true); mat4 eyeOffsets[2]; - auto baseProjection = renderArgs.getViewFrustum().getProjection(); - auto hmdInterface = DependencyManager::get(); - float IPDScale = hmdInterface->getIPDScale(); + mat4 eyeProjections[2]; // FIXME we probably don't need to set the projection matrix every frame, // only when the display plugin changes (or in non-HMD modes when the user @@ -2604,7 +2617,7 @@ void Application::paintGL() { // Grab the translation vec3 eyeOffset = glm::vec3(eyeToHead[3]); // Apply IPD scaling - mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); + mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * ipdScale); eyeOffsets[eye] = eyeOffsetTransform; eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); }); @@ -2624,8 +2637,14 @@ void Application::paintGL() { PerformanceTimer perfTimer("postComposite"); renderArgs._batch = &postCompositeBatch; renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height())); - renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView()); for_each_eye([&](Eye eye) { + + // apply eye offset and IPD scale to the view matrix + mat4 eyeToHead = displayPlugin->getEyeToHeadTransform(eye); + vec3 eyeOffset = glm::vec3(eyeToHead[3]); + mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * ipdScale); + renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView() * eyeOffsetTransform); + renderArgs._batch->setProjectionTransform(eyeProjections[eye]); _overlays.render3DHUDOverlays(&renderArgs); }); @@ -6411,7 +6430,12 @@ void Application::addAssetToWorldFromURL(QString url) { } if (url.contains("vr.google.com/downloads")) { filename = url.section('/', -1); - filename.remove(".zip"); + if (url.contains("noDownload")) { + filename.remove(".zip?noDownload=false"); + } else { + filename.remove(".zip"); + } + } if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { @@ -6441,7 +6465,11 @@ void Application::addAssetToWorldFromURLRequestFinished() { } if (url.contains("vr.google.com/downloads")) { filename = url.section('/', -1); - filename.remove(".zip"); + if (url.contains("noDownload")) { + filename.remove(".zip?noDownload=false"); + } else { + filename.remove(".zip"); + } isBlocks = true; } @@ -6608,7 +6636,9 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) { properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar. properties.setVisible(false); // Temporarily set so that don't see at large unresized dimensions. - properties.setPosition(getMyAvatar()->getPosition() + getMyAvatar()->getOrientation() * glm::vec3(0.0f, 0.0f, -2.0f)); + glm::vec3 positionOffset = getMyAvatar()->getOrientation() * (getMyAvatar()->getSensorToWorldScale() * glm::vec3(0.0f, 0.0f, -2.0f)); + properties.setPosition(getMyAvatar()->getPosition() + positionOffset); + properties.setRotation(getMyAvatar()->getOrientation()); properties.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); auto entityID = DependencyManager::get()->addEntity(properties); @@ -6655,7 +6685,7 @@ void Application::addAssetToWorldCheckModelSize() { if (dimensions != DEFAULT_DIMENSIONS) { // Scale model so that its maximum is exactly specific size. - const float MAXIMUM_DIMENSION = 1.0f; + const float MAXIMUM_DIMENSION = 1.0f * getMyAvatar()->getSensorToWorldScale(); auto previousDimensions = dimensions; auto scale = std::min(MAXIMUM_DIMENSION / dimensions.x, std::min(MAXIMUM_DIMENSION / dimensions.y, MAXIMUM_DIMENSION / dimensions.z)); diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index fe5355ff2e..a47bbbad1b 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -165,7 +165,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: Transform avatarTransform; avatarTransform = myAvatar->getTransform(); - palmPosition = avatarTransform.transform(camRelPos / myAvatar->getDomainLimitedScale()); + palmPosition = avatarTransform.transform(camRelPos); palmRotation = avatarTransform.getRotation() * camRelRot; } else { glm::vec3 avatarRigidBodyPosition; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5452fef4fd..f7116a60db 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -64,18 +64,13 @@ using namespace std; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; -const float MAX_WALKING_SPEED = 2.6f; // human walking speed -const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets additive boost below this speed -const float MIN_AVATAR_SPEED = 0.05f; -const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this - const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec -// TODO: normalize avatar speed for standard avatar size, then scale all motion logic -// to properly follow avatar size. -float MAX_AVATAR_SPEED = 30.0f; -float MAX_ACTION_MOTOR_SPEED = MAX_AVATAR_SPEED; +const float MAX_BOOST_SPEED = 0.5f * DEFAULT_AVATAR_MAX_WALKING_SPEED; // action motor gets additive boost below this speed +const float MIN_AVATAR_SPEED = 0.05f; +const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this + float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f; float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f; const int SCRIPTED_MOTOR_CAMERA_FRAME = 0; @@ -87,29 +82,6 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -// default values, used when avatar is missing joints... (avatar space) -static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f }; -static const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f }; -static const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f }; -static const glm::quat DEFAULT_AVATAR_RIGHTARM_ROT { -0.536241f, 0.536241f, -0.460918f, -0.460918f }; -static const glm::vec3 DEFAULT_AVATAR_LEFTARM_POS { 0.134795f, 0.396349f, -0.0515881f }; -static const glm::quat DEFAULT_AVATAR_LEFTARM_ROT { 0.536257f, 0.536258f, -0.460899f, 0.4609f }; -static const glm::vec3 DEFAULT_AVATAR_RIGHTHAND_POS { -0.72768f, 0.396349f, -0.0515779f }; -static const glm::quat DEFAULT_AVATAR_RIGHTHAND_ROT { 0.479184f, -0.520013f, 0.522537f, 0.476365f}; -static const glm::vec3 DEFAULT_AVATAR_LEFTHAND_POS { 0.727588f, 0.39635f, -0.0515878f }; -static const glm::quat DEFAULT_AVATAR_LEFTHAND_ROT { -0.479181f, -0.52001f, 0.52254f, -0.476369f }; -static const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.445f, 0.025f }; -static const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.32f, 0.02f }; -static const glm::quat DEFAULT_AVATAR_SPINE2_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.0f, 0.0f }; -static const glm::quat DEFAULT_AVATAR_HIPS_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.08f, -0.96f, 0.029f}; -static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.9154590368270874f, -0.005437685176730156f, -0.023744143545627594f }; -static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f }; -static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.9154615998268127f, 0.0053307069465518f, 0.023696165531873703f }; - MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), _yawSpeed(YAW_SPEED_DEFAULT), @@ -344,7 +316,7 @@ void MyAvatar::centerBody() { // transform this body into world space auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; auto worldBodyPos = extractTranslation(worldBodyMatrix); - auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + auto worldBodyRot = glmExtractRotation(worldBodyMatrix); if (_characterController.getState() == CharacterController::State::Ground) { // the avatar's physical aspect thinks it is standing on something @@ -397,7 +369,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { // transform this body into world space auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; auto worldBodyPos = extractTranslation(worldBodyMatrix); - auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + auto worldBodyRot = glmExtractRotation(worldBodyMatrix); // this will become our new position. setPosition(worldBodyPos); @@ -586,7 +558,7 @@ void MyAvatar::simulate(float deltaTime) { headPosition = getPosition(); } head->setPosition(headPosition); - head->setScale(getUniformScale()); + head->setScale(getModelScale()); head->simulate(deltaTime); } @@ -664,7 +636,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } _hmdSensorPosition = newHmdSensorPosition; - _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); + _hmdSensorOrientation = glmExtractRotation(hmdSensorMatrix); auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD); if (headPose.isValid()) { _headControllerFacing = getFacingDir2D(headPose.rotation); @@ -688,9 +660,11 @@ void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeV // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. void MyAvatar::updateSensorToWorldMatrix() { + // update the sensor mat so that the body position will end up in the desired // position when driven from the head. - glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition()); + float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); + glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getOrientation(), getPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); lateUpdatePalms(); @@ -1002,6 +976,7 @@ void MyAvatar::saveData() { settings.setValue("collisionSoundURL", _collisionSoundURL); settings.setValue("useSnapTurn", _useSnapTurn); settings.setValue("clearOverlayWhenMoving", _clearOverlayWhenMoving); + settings.setValue("userHeight", getUserHeight()); settings.endGroup(); } @@ -1142,6 +1117,7 @@ void MyAvatar::loadData() { setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool()); setClearOverlayWhenMoving(settings.value("clearOverlayWhenMoving", _clearOverlayWhenMoving).toBool()); setDominantHand(settings.value("dominantHand", _dominantHand).toString().toLower()); + setUserHeight(settings.value("userHeight", DEFAULT_AVATAR_HEIGHT).toDouble()); settings.endGroup(); setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible)); @@ -1247,7 +1223,7 @@ void MyAvatar::updateLookAtTargetAvatar() { float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); avatar->setIsLookAtTarget(false); if (!avatar->isMyAvatar() && avatar->isInitialized() && - (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getUniformScale())) { + (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getModelScale())) { float radius = glm::length(avatar->getHead()->getEyePosition() - avatar->getHead()->getRightEyePosition()); float angleTo = coneSphereAngle(getHead()->getEyePosition(), lookForward, avatar->getHead()->getEyePosition(), radius); if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { @@ -1489,7 +1465,7 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { void MyAvatar::rebuildCollisionShape() { // compute localAABox - float scale = getUniformScale(); + float scale = getModelScale(); float radius = scale * _skeletonModel->getBoundingCapsuleRadius(); float height = scale * _skeletonModel->getBoundingCapsuleHeight() + 2.0f * radius; glm::vec3 corner(-radius, -0.5f * height, -radius); @@ -1595,6 +1571,7 @@ void MyAvatar::prepareForPhysicsSimulation() { } _characterController.handleChangedCollisionGroup(); _characterController.setParentVelocity(parentVelocity); + _characterController.setScaleFactor(getSensorToWorldScale()); _characterController.setPositionAndOrientation(getPosition(), getOrientation()); auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); @@ -1930,7 +1907,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f; bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { - return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); + return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { @@ -1978,38 +1955,8 @@ void MyAvatar::updateOrientation(float deltaTime) { snapTurn = true; } - // use head/HMD orientation to turn while flying - if (getCharacterController()->getState() == CharacterController::State::Hover) { - - // This is the direction the user desires to fly in. - glm::vec3 desiredFacing = getMyHead()->getHeadOrientation() * Vectors::UNIT_Z; - desiredFacing.y = 0.0f; - - // This is our reference frame, it is captured when the user begins to move. - glm::vec3 referenceFacing = transformVectorFast(_sensorToWorldMatrix, _hoverReferenceCameraFacing); - referenceFacing.y = 0.0f; - referenceFacing = glm::normalize(referenceFacing); - glm::vec3 referenceRight(referenceFacing.z, 0.0f, -referenceFacing.x); - const float HOVER_FLY_ROTATION_PERIOD = 0.5f; - float tau = glm::clamp(deltaTime / HOVER_FLY_ROTATION_PERIOD, 0.0f, 1.0f); - - // new facing is a linear interpolation between the desired and reference vectors. - glm::vec3 newFacing = glm::normalize((1.0f - tau) * referenceFacing + tau * desiredFacing); - - // calcualte the signed delta yaw angle to apply so that we match our newFacing. - float sign = copysignf(1.0f, glm::dot(desiredFacing, referenceRight)); - float deltaAngle = sign * acosf(glm::clamp(glm::dot(referenceFacing, newFacing), -1.0f, 1.0f)); - - // speedFactor is 0 when we are at rest adn 1.0 when we are at max flying speed. - const float MAX_FLYING_SPEED = 30.0f; - float speedFactor = glm::min(glm::length(getVelocity()) / MAX_FLYING_SPEED, 1.0f); - - // apply our delta, but scale it by the speed factor, so we turn faster when we are flying faster. - totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI)); - } - - // Use head/HMD roll to turn while walking or flying, but not when standing still - if (qApp->isHMDMode() && _hmdRollControlEnabled && hasDriveInput()) { + // Use head/HMD roll to turn while flying, but not when standing still. + if (qApp->isHMDMode() && getCharacterController()->getState() == CharacterController::State::Hover && _hmdRollControlEnabled && hasDriveInput()) { // Turn with head roll. const float MIN_CONTROL_SPEED = 0.01f; float speed = glm::length(getVelocity()); @@ -2103,17 +2050,17 @@ void MyAvatar::updateActionMotor(float deltaTime) { if (state == CharacterController::State::Hover) { // we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed float motorSpeed = glm::length(_actionMotorVelocity); - float finalMaxMotorSpeed = getUniformScale() * MAX_ACTION_MOTOR_SPEED; + float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED; float speedGrowthTimescale = 2.0f; float speedIncreaseFactor = 1.8f; motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor; - const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED; + const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED; if (_isPushing) { if (motorSpeed < maxBoostSpeed) { // an active action motor should never be slower than this float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed; - motorSpeed += MIN_AVATAR_SPEED * boostCoefficient; + motorSpeed += getSensorToWorldScale() * MIN_AVATAR_SPEED * boostCoefficient; } else if (motorSpeed > finalMaxMotorSpeed) { motorSpeed = finalMaxMotorSpeed; } @@ -2121,7 +2068,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { _actionMotorVelocity = motorSpeed * direction; } else { // we're interacting with a floor --> simple horizontal speed and exponential decay - _actionMotorVelocity = MAX_WALKING_SPEED * direction; + _actionMotorVelocity = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_WALKING_SPEED * direction; } float boomChange = getDriveKey(ZOOM); @@ -2135,22 +2082,24 @@ void MyAvatar::updatePosition(float deltaTime) { } vec3 velocity = getVelocity(); + float sensorToWorldScale = getSensorToWorldScale(); + float sensorToWorldScale2 = sensorToWorldScale * sensorToWorldScale; const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s if (!_characterController.isEnabledAndReady()) { // _characterController is not in physics simulation but it can still compute its target velocity updateMotors(); _characterController.computeNewVelocity(deltaTime, velocity); - float speed2 = glm::length2(velocity); - if (speed2 > MIN_AVATAR_SPEED_SQUARED) { + float speed2 = glm::length(velocity); + if (speed2 > sensorToWorldScale2 * MIN_AVATAR_SPEED_SQUARED) { // update position ourselves applyPositionDelta(deltaTime * velocity); } measureMotionDerivatives(deltaTime); - _moving = speed2 > MOVING_SPEED_THRESHOLD_SQUARED; + _moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED; } else { float speed2 = glm::length2(velocity); - _moving = speed2 > MOVING_SPEED_THRESHOLD_SQUARED; + _moving = speed2 > sensorToWorldScale2 * MOVING_SPEED_THRESHOLD_SQUARED; if (_moving) { // scan for walkability @@ -2161,18 +2110,6 @@ void MyAvatar::updatePosition(float deltaTime) { _characterController.setStepUpEnabled(result.walkable); } } - - // capture the head rotation, in sensor space, when the user first indicates they would like to move/fly. - if (!_hoverReferenceCameraFacingIsCaptured && - (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) { - _hoverReferenceCameraFacingIsCaptured = true; - // transform the camera facing vector into sensor space. - _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), - getMyHead()->getHeadOrientation() * Vectors::UNIT_Z); - } else if (_hoverReferenceCameraFacingIsCaptured && - (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) { - _hoverReferenceCameraFacingIsCaptured = false; - } } void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) { @@ -2325,7 +2262,8 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings _targetScale = clampedScale; } - setScale(glm::vec3(_targetScale)); + setModelScale(_targetScale); + rebuildCollisionShape(); settings.endGroup(); } @@ -2711,11 +2649,30 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { // Y_180 is necessary because rig is z forward and hmdOrientation is -z forward glm::vec3 headToNeck = headOrientation * Quaternions::Y_180 * (localNeck - localHead); glm::vec3 neckToRoot = headOrientationYawOnly * Quaternions::Y_180 * -localNeck; - glm::vec3 bodyPos = headPosition + headToNeck + neckToRoot; + + float invSensorToWorldScale = getUserEyeHeight() / getEyeHeight(); + glm::vec3 bodyPos = headPosition + invSensorToWorldScale * (headToNeck + neckToRoot); return createMatFromQuatAndPos(headOrientationYawOnly, bodyPos); } +float MyAvatar::getUserHeight() const { + return _userHeight.get(); +} + +void MyAvatar::setUserHeight(float value) { + _userHeight.set(value); + + float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); + emit sensorToWorldScaleChanged(sensorToWorldScale); +} + +float MyAvatar::getUserEyeHeight() const { + float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + float userHeight = _userHeight.get(); + return userHeight - userHeight * ratio; +} + glm::vec3 MyAvatar::getPositionForAudio() { switch (_audioListenerMode) { case AudioListenerMode::FROM_HEAD: @@ -2834,7 +2791,6 @@ bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, co } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { - // -z axis of currentBodyMatrix in world space. glm::vec3 forward = glm::normalize(glm::vec3(-currentBodyMatrix[0][2], -currentBodyMatrix[1][2], -currentBodyMatrix[2][2])); // x axis of currentBodyMatrix in world space. @@ -2858,7 +2814,6 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, } bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { - const float CYLINDER_TOP = 0.1f; const float CYLINDER_BOTTOM = -1.5f; @@ -2886,6 +2841,10 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix; AnimPose followWorldPose(currentWorldMatrix); + + // remove scale present from sensorToWorldMatrix + followWorldPose.scale() = glm::vec3(1.0f); + if (isActive(Rotation)) { followWorldPose.rot() = glmExtractRotation(desiredWorldMatrix); } @@ -2910,16 +2869,16 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co // apply follow displacement to the body matrix. glm::vec3 worldLinearDisplacement = myAvatar.getCharacterController()->getFollowLinearDisplacement(); glm::quat worldAngularDisplacement = myAvatar.getCharacterController()->getFollowAngularDisplacement(); - glm::quat sensorToWorld = glmExtractRotation(myAvatar.getSensorToWorldMatrix()); - glm::quat worldToSensor = glm::inverse(sensorToWorld); - glm::vec3 sensorLinearDisplacement = worldToSensor * worldLinearDisplacement; - glm::quat sensorAngularDisplacement = worldToSensor * worldAngularDisplacement * sensorToWorld; + glm::mat4 sensorToWorldMatrix = myAvatar.getSensorToWorldMatrix(); + glm::mat4 worldToSensorMatrix = glm::inverse(sensorToWorldMatrix); + + glm::vec3 sensorLinearDisplacement = transformVectorFast(worldToSensorMatrix, worldLinearDisplacement); + glm::quat sensorAngularDisplacement = glmExtractRotation(worldToSensorMatrix) * worldAngularDisplacement * glmExtractRotation(sensorToWorldMatrix); glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix), sensorLinearDisplacement + extractTranslation(currentBodyMatrix)); return newBodyMat; - } else { return currentBodyMatrix; } @@ -2977,15 +2936,20 @@ glm::mat4 MyAvatar::computeCameraRelativeHandControllerMatrix(const glm::mat4& c cameraWorldMatrix *= createMatFromScaleQuatAndPos(vec3(-1.0f, 1.0f, 1.0f), glm::quat(), glm::vec3()); } - // compute a NEW sensorToWorldMatrix for the camera. - // The equation is cameraWorldMatrix = cameraSensorToWorldMatrix * _hmdSensorMatrix. - // here we solve for the unknown cameraSensorToWorldMatrix. - glm::mat4 cameraSensorToWorldMatrix = cameraWorldMatrix * glm::inverse(getHMDSensorMatrix()); + // move the camera into sensor space. + glm::mat4 cameraSensorMatrix = glm::inverse(getSensorToWorldMatrix()) * cameraWorldMatrix; - // Using the new cameraSensorToWorldMatrix, compute where the controller is in world space. - glm::mat4 controllerWorldMatrix = cameraSensorToWorldMatrix * controllerSensorMatrix; + // cancel out scale + glm::vec3 scale = extractScale(cameraSensorMatrix); + cameraSensorMatrix = glm::scale(cameraSensorMatrix, 1.0f / scale); - // move it into avatar space + // measure the offset from the hmd and the camera, in sensor space + glm::mat4 delta = cameraSensorMatrix * glm::inverse(getHMDSensorMatrix()); + + // apply the delta offset to the controller, in sensor space, then transform it into world space. + glm::mat4 controllerWorldMatrix = getSensorToWorldMatrix() * delta * controllerSensorMatrix; + + // transform controller into avatar space glm::mat4 avatarMatrix = createMatFromQuatAndPos(getOrientation(), getPosition()); return glm::inverse(avatarMatrix) * controllerWorldMatrix; } @@ -3257,3 +3221,12 @@ void MyAvatar::updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& const MyHead* MyAvatar::getMyHead() const { return static_cast(getHead()); } + +void MyAvatar::setModelScale(float scale) { + bool changed = (scale != getModelScale()); + Avatar::setModelScale(scale); + if (changed) { + float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); + emit sensorToWorldScaleChanged(sensorToWorldScale); + } +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 7cd8f9af32..c9d073cfd9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -103,6 +104,8 @@ class MyAvatar : public Avatar { * @property collisionsEnabled {bool} This can be used to disable collisions between the avatar and the world. * @property useAdvancedMovementControls {bool} Stores the user preference only, does not change user mappings, this is done in the defaultScript * "scripts/system/controllers/toggleAdvancedMovementForHandControllers.js". + * @property userHeight {number} The height of the user in sensor space. (meters). + * @property userEyeHeight {number} Estimated height of the users eyes in sensor space. (meters) */ // FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type @@ -147,6 +150,9 @@ class MyAvatar : public Avatar { Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) + Q_PROPERTY(float userHeight READ getUserHeight WRITE setUserHeight) + Q_PROPERTY(float userEyeHeight READ getUserEyeHeight) + const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -534,6 +540,10 @@ public: Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; + void setUserHeight(float value); + float getUserHeight() const; + float getUserEyeHeight() const; + public slots: void increaseSize(); void decreaseSize(); @@ -581,6 +591,8 @@ public slots: glm::vec3 getPositionForAudio(); glm::quat getOrientationForAudio(); + virtual void setModelScale(float scale) override; + signals: void audioListenerModeChanged(); void transformChanged(); @@ -593,6 +605,7 @@ signals: void wentActive(); void skeletonChanged(); void dominantHandChanged(const QString& hand); + void sensorToWorldScaleChanged(float sensorToWorldScale); private: @@ -713,7 +726,7 @@ private: float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; float _lastDrivenSpeed { 0.0f }; - // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access + // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; // cache of the current HMD sensor position and orientation in sensor space. @@ -779,8 +792,6 @@ private: AtRestDetector _hmdAtRestDetector; bool _lastIsMoving { false }; - bool _hoverReferenceCameraFacingIsCaptured { false }; - glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space // all poses are in sensor-frame std::map _controllerPoseMap; @@ -807,6 +818,9 @@ private: void setAway(bool value); std::vector _pinnedJoints; + + // height of user in sensor space, when standing erect. + ThreadSafeValueCache _userHeight { DEFAULT_AVATAR_HEIGHT }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 89e4368515..a707031167 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -148,7 +148,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); - auto velocity = myAvatar->getLocalVelocity(); + auto velocity = myAvatar->getLocalVelocity() / myAvatar->getSensorToWorldScale(); auto position = myAvatar->getLocalPosition(); auto orientation = myAvatar->getLocalOrientation(); _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); diff --git a/interface/src/raypick/JointRayPick.cpp b/interface/src/raypick/JointRayPick.cpp index 48ab908201..b4f0dde687 100644 --- a/interface/src/raypick/JointRayPick.cpp +++ b/interface/src/raypick/JointRayPick.cpp @@ -36,7 +36,7 @@ const PickRay JointRayPick::getPickRay(bool& valid) const { glm::quat rot = useAvatarHead ? jointRot * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT) : avatarRot * jointRot; // Apply offset - pos = pos + (rot * _posOffset); + pos = pos + (rot * (myAvatar->getSensorToWorldScale() * _posOffset)); glm::vec3 dir = rot * glm::normalize(_dirOffset); valid = true; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index f4adcc736d..9d64bcc476 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -143,15 +143,16 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter } if (!renderState.getEndID().isNull()) { QVariantMap endProps; + glm::quat faceAvatarRotation = DependencyManager::get()->getMyAvatar()->getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f))); if (_centerEndY) { endProps.insert("position", end); } else { glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value); - endProps.insert("position", vec3toVariant(endVec + glm::vec3(0, 0.5f * dim.y, 0))); + glm::vec3 currentUpVector = faceAvatarRotation * Vectors::UP; + endProps.insert("position", vec3toVariant(endVec + glm::vec3(currentUpVector.x * 0.5f * dim.y, currentUpVector.y * 0.5f * dim.y, currentUpVector.z * 0.5f * dim.y))); } if (_faceAvatar) { - glm::quat rotation = glm::inverse(glm::quat_cast(glm::lookAt(endVec, DependencyManager::get()->getMyAvatar()->getPosition(), Vectors::UP))); - endProps.insert("rotation", quatToVariant(glm::quat(glm::radians(glm::vec3(0, glm::degrees(safeEulerAngles(rotation)).y, 0))))); + endProps.insert("rotation", quatToVariant(faceAvatarRotation)); } endProps.insert("visible", true); endProps.insert("ignoreRayIntersection", renderState.doesEndIgnoreRays()); diff --git a/interface/src/raypick/RayPickScriptingInterface.cpp b/interface/src/raypick/RayPickScriptingInterface.cpp index 015f8fd7d1..cb2b3e4471 100644 --- a/interface/src/raypick/RayPickScriptingInterface.cpp +++ b/interface/src/raypick/RayPickScriptingInterface.cpp @@ -108,4 +108,4 @@ void RayPickScriptingInterface::setIgnoreAvatars(QUuid uid, const QScriptValue& void RayPickScriptingInterface::setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars) { qApp->getRayPickManager().setIncludeAvatars(uid, includeAvatars); -} \ No newline at end of file +} diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index 93c3a7652e..39d3164f1f 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -151,7 +151,7 @@ glm::vec3 HMDScriptingInterface::getPosition() const { glm::quat HMDScriptingInterface::getOrientation() const { if (qApp->getActiveDisplayPlugin()->isHmd()) { - return glm::normalize(glm::quat_cast(getWorldHMDMatrix())); + return glmExtractRotation(getWorldHMDMatrix()); } return glm::quat(); } diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 1c3df3210c..b8d4a38905 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -193,7 +193,7 @@ void setupPreferences() { preferences->addPreference(new PrimaryHandPreference(AVATAR_TUNING, "Dominant Hand", getter, setter)); } { - auto getter = [=]()->float { return myAvatar->getUniformScale(); }; + auto getter = [=]()->float { return myAvatar->getTargetScale(); }; auto setter = [=](float value) { myAvatar->setTargetScale(value); }; auto preference = new SpinnerSliderPreference(AVATAR_TUNING, "Avatar Scale", getter, setter); preference->setStep(0.05f); @@ -205,6 +205,16 @@ void setupPreferences() { // which can't be changed across domain switches. Having these values loaded up when you load the Dialog each time // is a way around this, therefore they're not specified here but in the QML. } + { + auto getter = [=]()->float { return myAvatar->getUserHeight(); }; + auto setter = [=](float value) { myAvatar->setUserHeight(value); }; + auto preference = new SpinnerPreference(AVATAR_TUNING, "User height (meters)", getter, setter); + preference->setMin(1.0f); + preference->setMax(2.2f); + preference->setDecimals(3); + preference->setStep(0.001f); + preferences->addPreference(preference); + } { auto getter = []()->float { return DependencyManager::get()->getEyeClosingThreshold(); }; auto setter = [](float value) { DependencyManager::get()->setEyeClosingThreshold(value); }; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 0cdcf012b8..e2a7df7ae6 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -46,13 +46,17 @@ void ModelOverlay::update(float deltatime) { if (_updateModel) { _updateModel = false; _model->setSnapModelToCenter(true); + Transform transform = getTransform(); +#ifndef USE_SN_SCALE + transform.setScale(1.0f); // disable inherited scale +#endif if (_scaleToFit) { - _model->setScaleToFit(true, getScale() * getDimensions()); + _model->setScaleToFit(true, transform.getScale() * getDimensions()); } else { - _model->setScale(getScale()); + _model->setScale(transform.getScale()); } - _model->setRotation(getRotation()); - _model->setTranslation(getPosition()); + _model->setRotation(transform.getRotation()); + _model->setTranslation(transform.getTranslation()); _model->setURL(_url); _model->simulate(deltatime, true); } else { @@ -93,13 +97,13 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { auto origPosition = getPosition(); auto origRotation = getRotation(); auto origDimensions = getDimensions(); - auto origScale = getScale(); + auto origScale = getSNScale(); Base3DOverlay::setProperties(properties); auto scale = properties["scale"]; if (scale.isValid()) { - setScale(vec3FromVariant(scale)); + setSNScale(vec3FromVariant(scale)); } auto dimensions = properties["dimensions"]; @@ -112,7 +116,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { _scaleToFit = false; } - if (origPosition != getPosition() || origRotation != getRotation() || origDimensions != getDimensions() || origScale != getScale()) { + if (origPosition != getPosition() || origRotation != getRotation() || origDimensions != getDimensions() || origScale != getSNScale()) { _updateModel = true; } @@ -194,7 +198,7 @@ QVariant ModelOverlay::getProperty(const QString& property) { return vec3toVariant(getDimensions()); } if (property == "scale") { - return vec3toVariant(getScale()); + return vec3toVariant(getSNScale()); } if (property == "textures") { if (_modelTextures.size() > 0) { diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index d6e0b61e7d..c93d225718 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -1045,6 +1045,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { AABox overlayFrameBox(low, dimensions); Transform overlayToWorldMatrix = overlay->getTransform(); + overlayToWorldMatrix.setScale(1.0f); // ignore inherited scale factor from parents glm::mat4 worldToOverlayMatrix = glm::inverse(overlayToWorldMatrix.getMatrix()); glm::vec3 overlayFrameSearchPosition = glm::vec3(worldToOverlayMatrix * glm::vec4(center, 1.0f)); glm::vec3 penetration; diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index f2684a4368..887bf7ff8e 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -74,7 +74,7 @@ namespace render { glm::vec3 myAvatarPosition = avatar->getPosition(); float angle = glm::degrees(glm::angle(myAvatarRotation)); glm::vec3 axis = glm::axis(myAvatarRotation); - float myAvatarScale = avatar->getUniformScale(); + float myAvatarScale = avatar->getModelScale(); Transform transform = Transform(); transform.setTranslation(myAvatarPosition); transform.setRotation(glm::angleAxis(angle, axis)); diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index e2877e1e07..e865714e58 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -69,8 +69,9 @@ bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve Transform Planar3DOverlay::evalRenderTransform() const { auto transform = getTransform(); + transform.setScale(1.0f); // ignore inherited scale factor from parents if (glm::length2(getDimensions()) != 1.0f) { transform.postScale(vec3(getDimensions(), 1.0f)); } return transform; -} \ No newline at end of file +} diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index d01da231cf..83dc4b0e2b 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -41,6 +41,9 @@ void Sphere3DOverlay::render(RenderArgs* args) { if (batch) { // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() Transform transform = getTransform(); +#ifndef USE_SN_SCALE + transform.setScale(1.0f); // ignore inherited scale from SpatiallyNestable +#endif transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE); batch->setModelTransform(transform); diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index ad61e28bc7..5e3e4ccee7 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -56,7 +56,11 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve float& distance, BoxFace& face, glm::vec3& surfaceNormal) { // extents is the entity relative, scaled, centered extents of the entity glm::mat4 worldToEntityMatrix; - getTransform().getInverseMatrix(worldToEntityMatrix); + Transform transform = getTransform(); +#ifndef USE_SN_SCALE + transform.setScale(1.0f); // ignore any inherited scale from SpatiallyNestable +#endif + transform.getInverseMatrix(worldToEntityMatrix); glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index eb8451e5c3..0807d1c117 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -200,7 +200,6 @@ QString Web3DOverlay::pickURL() { } } - void Web3DOverlay::loadSourceURL() { if (!_webSurface) { return; @@ -303,7 +302,6 @@ void Web3DOverlay::render(RenderArgs* args) { emit resizeWebSurface(); } - vec2 halfSize = getSize() / 2.0f; vec4 color(toGlm(getColor()), getAlpha()); @@ -320,6 +318,7 @@ void Web3DOverlay::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setResourceTexture(0, _texture); + auto renderTransform = getRenderTransform(); batch.setModelTransform(getRenderTransform()); auto geometryCache = DependencyManager::get(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index aa368b291b..fb255fd66d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1164,7 +1164,6 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh glm::vec3 localPoint = shapePose.inverse().xformPoint(point); // Only works for 14-dop shape infos. - assert(shapeInfo.dots.size() == DOP14_COUNT); if (shapeInfo.dots.size() != DOP14_COUNT) { return false; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index fc51501161..87d4a2d343 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -106,7 +107,7 @@ Avatar::Avatar(QThread* thread) : // we may have been created in the network thread, but we live in the main thread moveToThread(thread); - setScale(glm::vec3(1.0f)); // avatar scale is uniform + setModelScale(1.0f); // avatar scale is uniform auto geometryCache = DependencyManager::get(); _nameRectGeometryID = geometryCache->allocateID(); @@ -155,14 +156,14 @@ glm::vec3 Avatar::getNeckPosition() const { AABox Avatar::getBounds() const { if (!_skeletonModel->isRenderable() || _skeletonModel->needsFixupInScene()) { // approximately 2m tall, scaled to user request. - return AABox(getPosition() - glm::vec3(getUniformScale()), getUniformScale() * 2.0f); + return AABox(getPosition() - glm::vec3(getModelScale()), getModelScale() * 2.0f); } return _skeletonModel->getRenderableMeshBound(); } void Avatar::animateScaleChanges(float deltaTime) { if (_isAnimatingScale) { - float currentScale = getUniformScale(); + float currentScale = getModelScale(); float desiredScale = getDomainLimitedScale(); // use exponential decay toward the domain limit clamped scale @@ -177,7 +178,7 @@ void Avatar::animateScaleChanges(float deltaTime) { animatedScale = desiredScale; _isAnimatingScale = false; } - setScale(glm::vec3(animatedScale)); // avatar scale is uniform + setModelScale(animatedScale); // avatar scale is uniform // flag the joints as having changed for force update to RenderItem _hasNewJointData = true; @@ -364,7 +365,7 @@ void Avatar::simulate(float deltaTime, bool inView) { } head->setPosition(headPosition); } - head->setScale(getUniformScale()); + head->setScale(getModelScale()); head->simulate(deltaTime); } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. @@ -422,7 +423,7 @@ bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { glm::vec3 theirLookAt = dynamic_pointer_cast(avatar)->getHead()->getLookAtPosition(); glm::vec3 myEyePosition = getHead()->getEyePosition(); - return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getUniformScale()); + return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getModelScale()); } void Avatar::slamPosition(const glm::vec3& newPosition) { @@ -653,7 +654,7 @@ void Avatar::render(RenderArgs* renderArgs) { if (showCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); const float BOUNDING_SHAPE_ALPHA = 0.7f; - _skeletonModel->renderBoundingCollisionShapes(renderArgs, *renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); + _skeletonModel->renderBoundingCollisionShapes(renderArgs, *renderArgs->_batch, getModelScale(), BOUNDING_SHAPE_ALPHA); } if (showReceiveStats || showNamesAboveHeads) { @@ -726,9 +727,9 @@ void Avatar::simulateAttachments(float deltaTime) { } else { if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPosition) && _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRotation)) { - model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale()); + model->setTranslation(jointPosition + jointRotation * attachment.translation * getModelScale()); model->setRotation(jointRotation * attachment.rotation); - float scale = getUniformScale() * attachment.scale; + float scale = getModelScale() * attachment.scale; model->setScaleToFit(true, model->getNaturalDimensions() * scale, true); // hack to force rescale model->setSnapModelToCenter(false); // hack to force resnap model->setSnapModelToCenter(true); @@ -888,7 +889,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const } void Avatar::setSkeletonOffset(const glm::vec3& offset) { - const float MAX_OFFSET_LENGTH = getUniformScale() * 0.5f; + const float MAX_OFFSET_LENGTH = getModelScale() * 0.5f; float offsetLength = glm::length(offset); if (offsetLength > MAX_OFFSET_LENGTH) { _skeletonOffset = (MAX_OFFSET_LENGTH / offsetLength) * offset; @@ -986,14 +987,12 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { index += numeric_limits::max() + 1; // 65536 } - switch(index) { + switch (index) { case SENSOR_TO_WORLD_MATRIX_INDEX: { glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); - bool success; - Transform avatarTransform; - Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); - glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); - return glmExtractRotation(invAvatarMat * sensorToWorldMatrix); + glm::mat4 avatarMatrix = getLocalTransform().getMatrix(); + glm::mat4 finalMat = glm::inverse(avatarMatrix) * sensorToWorldMatrix; + return glmExtractRotation(finalMat); } case CONTROLLER_LEFTHAND_INDEX: { Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix()); @@ -1026,14 +1025,12 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { index += numeric_limits::max() + 1; // 65536 } - switch(index) { + switch (index) { case SENSOR_TO_WORLD_MATRIX_INDEX: { glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); - bool success; - Transform avatarTransform; - Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); - glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); - return extractTranslation(invAvatarMat * sensorToWorldMatrix); + glm::mat4 avatarMatrix = getLocalTransform().getMatrix(); + glm::mat4 finalMat = glm::inverse(avatarMatrix) * sensorToWorldMatrix; + return extractTranslation(finalMat); } case CONTROLLER_LEFTHAND_INDEX: { Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix()); @@ -1061,6 +1058,22 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { } } +glm::vec3 Avatar::getAbsoluteJointScaleInObjectFrame(int index) const { + if (index < 0) { + index += numeric_limits::max() + 1; // 65536 + } + + // only sensor to world matrix has non identity scale. + switch (index) { + case SENSOR_TO_WORLD_MATRIX_INDEX: { + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + return extractScale(sensorToWorldMatrix); + } + default: + return AvatarData::getAbsoluteJointScaleInObjectFrame(index); + } +} + void Avatar::invalidateJointIndicesCache() const { QWriteLocker writeLock(&_modelJointIndicesCacheLock); _modelJointsCached = false; @@ -1149,7 +1162,7 @@ glm::vec3 Avatar::getJointPosition(const QString& name) const { void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const { //Scale a world space vector as if it was relative to the position - positionToScale = getPosition() + getUniformScale() * (positionToScale - getPosition()); + positionToScale = getPosition() + getModelScale() * (positionToScale - getPosition()); } void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { @@ -1351,7 +1364,7 @@ void Avatar::updateDisplayNameAlpha(bool showDisplayName) { // virtual void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { - float uniformScale = getUniformScale(); + float uniformScale = getModelScale(); shapeInfo.setCapsuleY(uniformScale * _skeletonModel->getBoundingCapsuleRadius(), 0.5f * uniformScale * _skeletonModel->getBoundingCapsuleHeight()); shapeInfo.setOffset(uniformScale * _skeletonModel->getBoundingCapsuleOffset()); @@ -1548,3 +1561,54 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& addToScene(self, scene); } } + +float Avatar::getEyeHeight() const { + + if (QThread::currentThread() != thread()) { + float result = DEFAULT_AVATAR_EYE_HEIGHT; + BLOCKING_INVOKE_METHOD(const_cast(this), "getHeight", Q_RETURN_ARG(float, result)); + return result; + } + + // TODO: if performance becomes a concern we can cache this value rather then computing it everytime. + // Makes assumption that the y = 0 plane in geometry is the ground plane. + // We also make that assumption in Rig::computeAvatarBoundingCapsule() + float avatarScale = getModelScale(); + if (_skeletonModel) { + auto& rig = _skeletonModel->getRig(); + int headTopJoint = rig.indexOfJoint("HeadTop_End"); + int headJoint = rig.indexOfJoint("Head"); + int eyeJoint = rig.indexOfJoint("LeftEye") != -1 ? rig.indexOfJoint("LeftEye") : rig.indexOfJoint("RightEye"); + int toeJoint = rig.indexOfJoint("LeftToeBase") != -1 ? rig.indexOfJoint("LeftToeBase") : rig.indexOfJoint("RightToeBase"); + if (eyeJoint >= 0 && toeJoint >= 0) { + // measure from eyes to toes. + float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y; + return eyeHeight; + } else if (eyeJoint >= 0) { + // measure eyes to y = 0 plane. + float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; + float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - groundHeight; + return eyeHeight; + } else if (headTopJoint >= 0 && toeJoint >= 0) { + // measure toe to top of head. Note: default poses already include avatar scale factor + const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + float height = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y; + return height - height * ratio; + } else if (headTopJoint >= 0) { + const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; + float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; + float headHeight = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - groundHeight; + return headHeight - headHeight * ratio; + } else if (headJoint >= 0) { + float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; + const float DEFAULT_AVATAR_NECK_TO_EYE = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; + const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT; + float neckHeight = rig.getAbsoluteDefaultPose(headJoint).trans().y - groundHeight; + return neckHeight + neckHeight * ratio; + } else { + return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT; + } + } else { + return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT; + } +} diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c262ce2009..a5ec307c50 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -108,7 +108,6 @@ public: SkeletonModelPointer getSkeletonModel() { return _skeletonModel; } const SkeletonModelPointer getSkeletonModel() const { return _skeletonModel; } glm::vec3 getChestPosition() const; - float getUniformScale() const { return getScale().y; } const Head* getHead() const { return static_cast(_headData); } Head* getHead() { return static_cast(_headData); } @@ -151,6 +150,7 @@ public: */ Q_INVOKABLE virtual glm::vec3 getAbsoluteDefaultJointTranslationInObjectFrame(int index) const; + virtual glm::vec3 getAbsoluteJointScaleInObjectFrame(int index) const override; virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } @@ -232,6 +232,7 @@ public: void animateScaleChanges(float deltaTime); void setTargetScale(float targetScale) override; + float getTargetScale() const { return _targetScale; } Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const; @@ -254,6 +255,16 @@ public: bool isFading() const { return _isFading; } void updateFadingStatus(render::ScenePointer scene); + /**jsdoc + * Provides read only access to the current eye height of the avatar. + * @function Avatar.getEyeHeight + * @returns {number} eye height of avatar in meters + */ + Q_INVOKABLE float getEyeHeight() const; + + virtual float getModelScale() const { return _modelScale; } + virtual void setModelScale(float scale) { _modelScale = scale; } + public slots: // FIXME - these should be migrated to use Pose data instead @@ -356,6 +367,7 @@ private: bool _isAnimatingScale { false }; bool _mustFadeIn { false }; bool _isFading { false }; + float _modelScale { 1.0f }; static int _jointConesID; @@ -365,7 +377,6 @@ private: float _displayNameTargetAlpha { 1.0f }; float _displayNameAlpha { 1.0f }; - }; #endif // hifi_Avatar_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index c0d5fc07d7..eaa62ecb0a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -121,7 +121,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { void SkeletonModel::updateAttitude(const glm::quat& orientation) { setTranslation(_owningAvatar->getSkeletonPosition()); setRotation(orientation * Quaternions::Y_180); - setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); + setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getModelScale()); } // Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed), @@ -294,7 +294,7 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco } glm::vec3 SkeletonModel::getDefaultEyeModelPosition() const { - return _owningAvatar->getScale() * _defaultEyeModelPosition; + return _owningAvatar->getModelScale() * _defaultEyeModelPosition; } float DENSITY_OF_WATER = 1000.0f; // kg/m^3 @@ -316,7 +316,7 @@ void SkeletonModel::computeBoundingShape() { float radius, height; glm::vec3 offset; _rig.computeAvatarBoundingCapsule(geometry, radius, height, offset); - float invScale = 1.0f / _owningAvatar->getUniformScale(); + float invScale = 1.0f / _owningAvatar->getModelScale(); _boundingCapsuleRadius = invScale * radius; _boundingCapsuleHeight = invScale * height; _boundingCapsuleLocalOffset = invScale * offset; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1baf649e64..16245601dd 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2345,6 +2345,11 @@ glm::mat4 AvatarData::getSensorToWorldMatrix() const { return _sensorToWorldMatrixCache.get(); } +// thread-safe +float AvatarData::getSensorToWorldScale() const { + return extractUniformScale(_sensorToWorldMatrixCache.get()); +} + // thread-safe glm::mat4 AvatarData::getControllerLeftHandMatrix() const { return _controllerLeftHandMatrixCache.get(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b4c36dba70..664ef64bde 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -387,6 +387,8 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(glm::mat4 controllerLeftHandMatrix READ getControllerLeftHandMatrix) Q_PROPERTY(glm::mat4 controllerRightHandMatrix READ getControllerRightHandMatrix) + Q_PROPERTY(float sensorToWorldScale READ getSensorToWorldScale) + public: virtual QString getName() const override { return QString("Avatar:") + _displayName; } @@ -621,6 +623,7 @@ public: // thread safe Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; + Q_INVOKABLE float getSensorToWorldScale() const; Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const; Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const; diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index f09b3d7109..2f57cc29d0 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -339,23 +340,29 @@ void CompositorHelper::computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3& glm::mat4 CompositorHelper::getUiTransform() const { glm::mat4 modelMat; _modelTransform.getMatrix(modelMat); - return _currentCamera * glm::inverse(_currentDisplayPlugin->getHeadPose()) * modelMat; + return _sensorToWorldMatrix * modelMat; } //Finds the collision point of a world space ray bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { - auto UITransform = getUiTransform(); - auto relativePosition4 = glm::inverse(UITransform) * vec4(position, 1); - auto relativePosition = vec3(relativePosition4) / relativePosition4.w; - auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction; + glm::mat4 uiToWorld = getUiTransform(); + glm::mat4 worldToUi = glm::inverse(uiToWorld); + glm::vec3 localPosition = transformPoint(worldToUi, position); + glm::vec3 localDirection = glm::normalize(transformVectorFast(worldToUi, direction)); - const float UI_RADIUS = 1.0f; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale + const float UI_RADIUS = 1.0f; float instersectionDistance; - if (raySphereIntersect(relativeDirection, relativePosition, UI_RADIUS, &instersectionDistance)){ - result = position + glm::normalize(direction) * instersectionDistance; + if (raySphereIntersect(localDirection, localPosition, UI_RADIUS, &instersectionDistance)) { + result = transformPoint(uiToWorld, localPosition + localDirection * instersectionDistance); +#ifdef WANT_DEBUG + DebugDraw::getInstance().drawRay(position, result, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); +#endif return true; + } else { +#ifdef WANT_DEBUG + DebugDraw::getInstance().drawRay(position, position + (direction * 1000.0f), glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); +#endif } - return false; } diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index 946229fc9b..b1d2815f65 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -109,7 +109,10 @@ public: void setReticleOverDesktop(bool value) { _isOverDesktop = value; } void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; } - void setFrameInfo(uint32_t frame, const glm::mat4& camera) { _currentCamera = camera; } + void setFrameInfo(uint32_t frame, const glm::mat4& camera, const glm::mat4& sensorToWorldMatrix) { + _currentCamera = camera; + _sensorToWorldMatrix = sensorToWorldMatrix; + } signals: void allowMouseCaptureChanged(); @@ -124,6 +127,7 @@ private: DisplayPluginPointer _currentDisplayPlugin; glm::mat4 _currentCamera; + glm::mat4 _sensorToWorldMatrix; QWidget* _renderingWidget{ nullptr }; //// Support for hovering and tooltips diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 683c910b7c..db4e0ff7a1 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -27,8 +27,8 @@ public: bool isHmd() const override final { return true; } float getIPD() const override final { return _ipd; } glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; } - glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override final { return _eyeProjections[eye]; } - glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; } + glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override { return _eyeProjections[eye]; } + glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override { return _cullingProjection; } glm::uvec2 getRecommendedUiSize() const override final; glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; } bool isDisplayVisible() const override { return isHmdMounted(); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c6bad008e4..2508b598af 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -36,6 +36,29 @@ static CollisionRenderMeshCache collisionMeshCache; +void ModelEntityWrapper::setModel(const ModelPointer& model) { + withWriteLock([&] { + if (_model != model) { + _model = model; + if (_model) { + _needsInitialSimulation = true; + } + } + }); +} + +ModelPointer ModelEntityWrapper::getModel() const { + return resultWithReadLock([&] { + return _model; + }); +} + +bool ModelEntityWrapper::isModelLoaded() const { + return resultWithReadLock([&] { + return _model.operator bool() && _model->isLoaded(); + }); +} + EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity{ new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()) }; entity->setProperties(properties); @@ -43,7 +66,7 @@ EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityI } RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID, bool dimensionsInitialized) : - ModelEntityItem(entityItemID), + ModelEntityWrapper(entityItemID), _dimensionsInitialized(dimensionsInitialized) { } @@ -83,41 +106,47 @@ QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextu } void RenderableModelEntityItem::doInitialModelSimulation() { + ModelPointer model = getModel(); + if (!model) { + return; + } // The machinery for updateModelBounds will give existing models the opportunity to fix their // translation/rotation/scale/registration. The first two are straightforward, but the latter two have guards to // make sure they don't happen after they've already been set. Here we reset those guards. This doesn't cause the // entity values to change -- it just allows the model to match once it comes in. - _model->setScaleToFit(false, getDimensions()); - _model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); + model->setScaleToFit(false, getDimensions()); + model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); // now recalculate the bounds and registration - _model->setScaleToFit(true, getDimensions()); - _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); - _model->setRotation(getRotation()); - _model->setTranslation(getPosition()); + model->setScaleToFit(true, getDimensions()); + model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); + model->setRotation(getRotation()); + model->setTranslation(getPosition()); { - PerformanceTimer perfTimer("_model->simulate"); - _model->simulate(0.0f); + PerformanceTimer perfTimer("model->simulate"); + model->simulate(0.0f); } _needsInitialSimulation = false; } void RenderableModelEntityItem::autoResizeJointArrays() { - if (_model && _model->isLoaded() && !_needsInitialSimulation) { - resizeJointArrays(_model->getJointStateCount()); + ModelPointer model = getModel(); + if (model && model->isLoaded() && !_needsInitialSimulation) { + resizeJointArrays(model->getJointStateCount()); } } bool RenderableModelEntityItem::needsUpdateModelBounds() const { - if (!hasModel() || !_model) { + ModelPointer model = getModel(); + if (!hasModel() || !model) { return false; } - if (!_dimensionsInitialized || !_model->isActive()) { + if (!_dimensionsInitialized || !model->isActive()) { return false; } - if (_model->needsReload()) { + if (model->needsReload()) { return true; } @@ -129,21 +158,21 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const { return true; } - if (_model->getScaleToFitDimensions() != getDimensions()) { + if (model->getScaleToFitDimensions() != getDimensions()) { return true; } - if (_model->getRegistrationPoint() != getRegistrationPoint()) { + if (model->getRegistrationPoint() != getRegistrationPoint()) { return true; } bool success; auto transform = getTransform(success); if (success) { - if (_model->getTranslation() != transform.getTranslation()) { + if (model->getTranslation() != transform.getTranslation()) { return true; } - if (_model->getRotation() != transform.getRotation()) { + if (model->getRotation() != transform.getRotation()) { return true; } } @@ -158,16 +187,6 @@ void RenderableModelEntityItem::updateModelBounds() { } } -void RenderableModelEntityItem::setModel(const ModelPointer& model) { - withWriteLock([&] { - if (_model != model) { - _model = model; - if (_model) { - _needsInitialSimulation = true; - } - } - }); -} EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = ModelEntityItem::getProperties(desiredProperties); // get the properties from our base class @@ -175,43 +194,44 @@ EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlag properties.setTextureNames(_originalTextures); } - if (_model) { - properties.setRenderInfoVertexCount(_model->getRenderInfoVertexCount()); - properties.setRenderInfoTextureCount(_model->getRenderInfoTextureCount()); - properties.setRenderInfoTextureSize(_model->getRenderInfoTextureSize()); - properties.setRenderInfoDrawCalls(_model->getRenderInfoDrawCalls()); - properties.setRenderInfoHasTransparent(_model->getRenderInfoHasTransparent()); + ModelPointer model = getModel(); + if (model) { + properties.setRenderInfoVertexCount(model->getRenderInfoVertexCount()); + properties.setRenderInfoTextureCount(model->getRenderInfoTextureCount()); + properties.setRenderInfoTextureSize(model->getRenderInfoTextureSize()); + properties.setRenderInfoDrawCalls(model->getRenderInfoDrawCalls()); + properties.setRenderInfoHasTransparent(model->getRenderInfoHasTransparent()); + + if (model->isLoaded()) { + // TODO: improve naturalDimensions in the future, + // for now we've added this hack for setting natural dimensions of models + Extents meshExtents = model->getFBXGeometry().getUnscaledMeshExtents(); + properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); + properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); + } } - if (_model && _model->isLoaded()) { - // TODO: improve naturalDimensions in the future, - // for now we've added this hack for setting natural dimensions of models - Extents meshExtents = _model->getFBXGeometry().getUnscaledMeshExtents(); - properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); - properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); - } return properties; } bool RenderableModelEntityItem::supportsDetailedRayIntersection() const { - return resultWithReadLock([&] { - return _model && _model->isLoaded(); - }); + return isModelLoaded(); } bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const { - if (!_model) { + auto model = getModel(); + if (!model) { return true; } // qCDebug(entitiesrenderer) << "RenderableModelEntityItem::findDetailedRayIntersection() precisionPicking:" // << precisionPicking; QString extraInfo; - return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, + return model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo, precisionPicking, false); } @@ -242,7 +262,7 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { // parse it twice. auto currentCompoundShapeURL = getCompoundShapeURL(); ModelEntityItem::setCompoundShapeURL(url); - if (getCompoundShapeURL() != currentCompoundShapeURL || !_model) { + if (getCompoundShapeURL() != currentCompoundShapeURL || !getModel()) { if (getShapeType() == SHAPE_TYPE_COMPOUND) { getCollisionGeometryResource(); } @@ -252,17 +272,18 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { bool RenderableModelEntityItem::isReadyToComputeShape() const { ShapeType type = getShapeType(); + auto model = getModel(); if (type == SHAPE_TYPE_COMPOUND) { - if (!_model || getCompoundShapeURL().isEmpty()) { + if (!model || getCompoundShapeURL().isEmpty()) { return false; } - if (_model->getURL().isEmpty()) { + if (model->getURL().isEmpty()) { // we need a render geometry with a scale to proceed, so give up. return false; } - if (_model->isLoaded()) { + if (model->isLoaded()) { if (!getCompoundShapeURL().isEmpty() && !_compoundShapeResource) { const_cast(this)->getCollisionGeometryResource(); } @@ -281,7 +302,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() const { // the model is still being downloaded. return false; } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { - return (_model && _model->isLoaded()); + return isModelLoaded(); } return true; } @@ -292,6 +313,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ShapeType type = getShapeType(); glm::vec3 dimensions = getDimensions(); + auto model = getModel(); if (type == SHAPE_TYPE_COMPOUND) { updateModelBounds(); @@ -373,14 +395,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / _model->getFBXGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / model->getFBXGeometry().getUnscaledMeshExtents().size(); // multiply each point by scale before handing the point-set off to the physics engine. // also determine the extents of the collision model. glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()); for (int32_t i = 0; i < pointCollection.size(); i++) { for (int32_t j = 0; j < pointCollection[i].size(); j++) { // back compensate for registration so we can apply that offset to the shapeInfo later - pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + _model->getOffset()) - registrationOffset; + pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + model->getOffset()) - registrationOffset; } } shapeInfo.setParams(type, dimensions, getCompoundShapeURL()); @@ -389,11 +411,11 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { assert(_model && _model->isLoaded()); updateModelBounds(); - _model->updateGeometry(); + model->updateGeometry(); // compute meshPart local transforms QVector localTransforms; - const FBXGeometry& fbxGeometry = _model->getFBXGeometry(); + const FBXGeometry& fbxGeometry = model->getFBXGeometry(); int numFbxMeshes = fbxGeometry.meshes.size(); int totalNumVertices = 0; glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); @@ -401,7 +423,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.clusters.size() > 0) { const FBXCluster& cluster = mesh.clusters.at(0); - auto jointMatrix = _model->getRig().getJointTransform(cluster.jointIndex); + auto jointMatrix = model->getRig().getJointTransform(cluster.jointIndex); // we backtranslate by the registration offset so we can apply that offset to the shapeInfo later localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix); } else { @@ -417,7 +439,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { return; } - auto& meshes = _model->getGeometry()->getMeshes(); + auto& meshes = model->getGeometry()->getMeshes(); int32_t numMeshes = (int32_t)(meshes.size()); const int MAX_ALLOWED_MESH_COUNT = 1000; @@ -631,7 +653,8 @@ void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) } bool RenderableModelEntityItem::contains(const glm::vec3& point) const { - if (EntityItem::contains(point) && _model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { + auto model = getModel(); + if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { return _compoundShapeResource->getFBXGeometry().convexHullContains(worldToEntity(point)); } @@ -639,11 +662,12 @@ bool RenderableModelEntityItem::contains(const glm::vec3& point) const { } bool RenderableModelEntityItem::shouldBePhysical() const { + auto model = getModel(); // If we have a model, make sure it hasn't failed to download. // If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready. - if (_model && getShapeType() == SHAPE_TYPE_COMPOUND && _model->didCollisionGeometryRequestFail()) { + if (model && getShapeType() == SHAPE_TYPE_COMPOUND && model->didCollisionGeometryRequestFail()) { return false; - } else if (_model && getShapeType() != SHAPE_TYPE_NONE && _model->didVisualGeometryRequestFail()) { + } else if (model && getShapeType() != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) { return false; } else { return ModelEntityItem::shouldBePhysical(); @@ -651,9 +675,10 @@ bool RenderableModelEntityItem::shouldBePhysical() const { } glm::quat RenderableModelEntityItem::getAbsoluteJointRotationInObjectFrame(int index) const { - if (_model) { + auto model = getModel(); + if (model) { glm::quat result; - if (_model->getAbsoluteJointRotationInRigFrame(index, result)) { + if (model->getAbsoluteJointRotationInRigFrame(index, result)) { return result; } } @@ -661,9 +686,10 @@ glm::quat RenderableModelEntityItem::getAbsoluteJointRotationInObjectFrame(int i } glm::vec3 RenderableModelEntityItem::getAbsoluteJointTranslationInObjectFrame(int index) const { - if (_model) { + auto model = getModel(); + if (model) { glm::vec3 result; - if (_model->getAbsoluteJointTranslationInRigFrame(index, result)) { + if (model->getAbsoluteJointTranslationInRigFrame(index, result)) { return result; } } @@ -671,10 +697,11 @@ glm::vec3 RenderableModelEntityItem::getAbsoluteJointTranslationInObjectFrame(in } bool RenderableModelEntityItem::setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) { - if (!_model) { + auto model = getModel(); + if (!model) { return false; } - const Rig& rig = _model->getRig(); + const Rig& rig = model->getRig(); int jointParentIndex = rig.getJointParentIndex(index); if (jointParentIndex == -1) { return setLocalJointRotation(index, rotation); @@ -700,10 +727,11 @@ bool RenderableModelEntityItem::setAbsoluteJointRotationInObjectFrame(int index, } bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) { - if (!_model) { + auto model = getModel(); + if (!model) { return false; } - const Rig& rig = _model->getRig(); + const Rig& rig = model->getRig(); int jointParentIndex = rig.getJointParentIndex(index); if (jointParentIndex == -1) { @@ -730,9 +758,10 @@ bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int ind } glm::quat RenderableModelEntityItem::getLocalJointRotation(int index) const { - if (_model) { + auto model = getModel(); + if (model) { glm::quat result; - if (_model->getJointRotation(index, result)) { + if (model->getJointRotation(index, result)) { return result; } } @@ -740,9 +769,10 @@ glm::quat RenderableModelEntityItem::getLocalJointRotation(int index) const { } glm::vec3 RenderableModelEntityItem::getLocalJointTranslation(int index) const { - if (_model) { + auto model = getModel(); + if (model) { glm::vec3 result; - if (_model->getJointTranslation(index, result)) { + if (model->getJointTranslation(index, result)) { return result; } } @@ -810,19 +840,22 @@ void RenderableModelEntityItem::setJointTranslationsSet(const QVector& tra void RenderableModelEntityItem::locationChanged(bool tellPhysics) { PerformanceTimer pertTimer("locationChanged"); EntityItem::locationChanged(tellPhysics); - if (_model && _model->isLoaded()) { - _model->updateRenderItems(); + auto model = getModel(); + if (model && model->isLoaded()) { + model->updateRenderItems(); } } int RenderableModelEntityItem::getJointIndex(const QString& name) const { - return (_model && _model->isActive()) ? _model->getRig().indexOfJoint(name) : -1; + auto model = getModel(); + return (model && model->isActive()) ? model->getRig().indexOfJoint(name) : -1; } QStringList RenderableModelEntityItem::getJointNames() const { QStringList result; - if (_model && _model->isActive()) { - const Rig& rig = _model->getRig(); + auto model = getModel(); + if (model && model->isActive()) { + const Rig& rig = model->getRig(); int jointCount = rig.getJointStateCount(); for (int jointIndex = 0; jointIndex < jointCount; jointIndex++) { result << rig.nameOfJoint(jointIndex); @@ -832,19 +865,16 @@ QStringList RenderableModelEntityItem::getJointNames() const { } bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { - if (!_model || !_model->isLoaded()) { + auto model = getModel(); + if (!model || !model->isLoaded()) { return false; } - BLOCKING_INVOKE_METHOD(_model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result)); + BLOCKING_INVOKE_METHOD(model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result)); return !result.isEmpty(); } void RenderableModelEntityItem::copyAnimationJointDataToModel() { - ModelPointer model; - withReadLock([&] { - model = _model; - }); - + auto model = getModel(); if (!model || !model->isLoaded()) { return; } @@ -859,7 +889,7 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { jointData.rotationDirty = false; } if (jointData.translationDirty) { - _model->setJointTranslation(index, true, jointData.joint.translation, 1.0f); + model->setJointTranslation(index, true, jointData.joint.translation, 1.0f); jointData.translationDirty = false; } } @@ -1192,7 +1222,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating // we will watch for that and ask the model to update it's render items if (model->getRenderItemsNeedUpdate()) { - qDebug() << "QQQ" << __FUNCTION__ << "Update model render items" << model->getURL(); model->updateRenderItems(); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index d8ecbeb282..b9c751761d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -34,10 +34,24 @@ class ModelEntityRenderer; } } //#define MODEL_ENTITY_USE_FADE_EFFECT - -class RenderableModelEntityItem : public ModelEntityItem { +class ModelEntityWrapper : public ModelEntityItem { + using Parent = ModelEntityItem; friend class render::entities::ModelEntityRenderer; +protected: + ModelEntityWrapper(const EntityItemID& entityItemID) : Parent(entityItemID) {} + void setModel(const ModelPointer& model); + ModelPointer getModel() const; + bool isModelLoaded() const; + + bool _needsInitialSimulation{ true }; +private: + ModelPointer _model; +}; + +class RenderableModelEntityItem : public ModelEntityWrapper { + friend class render::entities::ModelEntityRenderer; + using Parent = ModelEntityWrapper; public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -97,14 +111,11 @@ private: bool isAnimatingSomething() const; void autoResizeJointArrays(); void copyAnimationJointDataToModel(); - void setModel(const ModelPointer& model); void getCollisionGeometryResource(); GeometryResource::Pointer _compoundShapeResource; bool _originalTexturesRead { false }; QVariantMap _originalTextures; - ModelPointer _model; - bool _needsInitialSimulation { true }; bool _dimensionsInitialized { true }; bool _needsJointSimulation { false }; bool _showCollisionGeometry { false }; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index be76504c80..8fcf2c090d 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -98,15 +98,15 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint } _shape = entity->getShape(); + _position = entity->getPosition(); + _dimensions = entity->getDimensions(); + _orientation = entity->getOrientation(); if (_shape == entity::Sphere) { _modelTransform.postScale(SPHERE_ENTITY_SCALE); } - - _position = entity->getPosition(); - _dimensions = entity->getDimensions(); - _orientation = entity->getOrientation(); + _modelTransform.postScale(_dimensions); } bool ShapeEntityRenderer::isTransparent() const { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5f9bd7e7ea..107af837fe 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -42,7 +42,8 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : setLocalVelocity(ENTITY_ITEM_DEFAULT_VELOCITY); setLocalAngularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY); // explicitly set transform parts to set dirty flags used by batch rendering - setScale(ENTITY_ITEM_DEFAULT_DIMENSIONS); + locationChanged(); + dimensionsChanged(); quint64 now = usecTimestampNow(); _lastSimulated = now; _lastUpdated = now; @@ -1376,7 +1377,11 @@ void EntityItem::setDimensions(const glm::vec3& value) { if (value.x <= 0.0f || value.y <= 0.0f || value.z <= 0.0f) { return; } - setScale(value); + if (_dimensions != value) { + _dimensions = value; + locationChanged(); + dimensionsChanged(); + } } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 84742587e9..a95f7ba316 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -179,7 +179,7 @@ public: void setDescription(const QString& value); /// Dimensions in meters (0.0 - TREE_SCALE) - inline const glm::vec3 getDimensions() const { return getScale(); } + inline const glm::vec3 getDimensions() const { return _dimensions; } virtual void setDimensions(const glm::vec3& value); float getLocalRenderAlpha() const; @@ -470,6 +470,7 @@ protected: virtual void dimensionsChanged() override; + glm::vec3 _dimensions { ENTITY_ITEM_DEFAULT_DIMENSIONS }; EntityTypes::EntityType _type { EntityTypes::Unknown }; quint64 _lastSimulated { 0 }; // last time this entity called simulate(), this includes velocity, angular velocity, // and physics changes diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 23a5c88900..ad9686bdf2 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -176,7 +176,7 @@ void PolyLineEntityItem::calculateScaleAndRegistrationPoint() { } // if Polyline has only one or fewer points, use default dimension settings - SpatiallyNestable::setScale(newScale); + setDimensions(newScale); EntityItem::setRegistrationPoint(newRegistrationPoint); } diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 4860e3d4a4..9fbc6c60a1 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -91,8 +91,6 @@ class PolyLineEntityItem : public EntityItem { // disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain virtual void setRegistrationPoint(const glm::vec3& value) override {}; - virtual void setScale(const glm::vec3& scale) override {}; - virtual void setScale(float value) override {}; virtual void debugDump() const override; static const float DEFAULT_LINE_WIDTH; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 9a7abc4e98..d2da521768 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -12,14 +12,13 @@ #include "CharacterController.h" #include +#include #include "ObjectMotionState.h" #include "PhysicsHelpers.h" #include "PhysicsLogging.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); -const float JUMP_SPEED = 3.5f; -const float MAX_FALL_HEIGHT = 20.0f; #ifdef DEBUG_STATE_CHANGE #define SET_STATE(desiredState, reason) setState(desiredState, reason) @@ -62,12 +61,11 @@ CharacterController::CharacterMotor::CharacterMotor(const glm::vec3& vel, const } CharacterController::CharacterController() { - _floorDistance = MAX_FALL_HEIGHT; + _floorDistance = _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT; _targetVelocity.setValue(0.0f, 0.0f, 0.0f); _followDesiredBodyTransform.setIdentity(); _followTimeRemaining = 0.0f; - _jumpSpeed = JUMP_SPEED; _state = State::Hover; _isPushingUp = false; _rayHitStartTime = 0; @@ -265,13 +263,13 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar btVector3 endPos = startPos + linearDisplacement; btQuaternion startRot = bodyTransform.getRotation(); - glm::vec2 currentFacing = getFacingDir2D(bulletToGLM(startRot)); - glm::vec2 currentRight(currentFacing.y, -currentFacing.x); - glm::vec2 desiredFacing = getFacingDir2D(bulletToGLM(_followDesiredBodyTransform.getRotation())); - float deltaAngle = acosf(glm::clamp(glm::dot(currentFacing, desiredFacing), -1.0f, 1.0f)); - float angularSpeed = deltaAngle / _followTimeRemaining; - float sign = copysignf(1.0f, glm::dot(desiredFacing, currentRight)); - btQuaternion angularDisplacement = btQuaternion(btVector3(0.0f, 1.0f, 0.0f), sign * angularSpeed * dt); + btQuaternion desiredRot = _followDesiredBodyTransform.getRotation(); + if (desiredRot.dot(startRot) < 0.0f) { + desiredRot = -desiredRot; + } + btQuaternion deltaRot = desiredRot * startRot.inverse(); + float angularSpeed = deltaRot.getAngle() / _followTimeRemaining; + btQuaternion angularDisplacement = btQuaternion(deltaRot.getAxis(), angularSpeed * dt); btQuaternion endRot = angularDisplacement * startRot; // in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. @@ -376,8 +374,7 @@ void CharacterController::updateGravity() { if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { _gravity = 0.0f; } else { - const float DEFAULT_CHARACTER_GRAVITY = -5.0f; - _gravity = DEFAULT_CHARACTER_GRAVITY; + _gravity = DEFAULT_AVATAR_GRAVITY; } if (_rigidBody) { _rigidBody->setGravity(_gravity * _currentUp); @@ -653,7 +650,7 @@ void CharacterController::updateState() { const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; - const btScalar MIN_HOVER_HEIGHT = 2.5f; + const btScalar MIN_HOVER_HEIGHT = _scaleFactor * DEFAULT_AVATAR_MIN_HOVER_HEIGHT; const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; // scan for distant floor @@ -663,7 +660,7 @@ void CharacterController::updateState() { btScalar rayLength = _radius; int16_t collisionGroup = computeCollisionGroup(); if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { - rayLength += MAX_FALL_HEIGHT; + rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT; } else { rayLength += MIN_HOVER_HEIGHT; } @@ -717,11 +714,15 @@ void CharacterController::updateState() { SET_STATE(State::Hover, "no ground"); } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { SET_STATE(State::InAir, "takeoff done"); - velocity += _jumpSpeed * _currentUp; + + // compute jumpSpeed based on the scaled jump height for the default avatar in default gravity. + float jumpSpeed = sqrtf(2.0f * DEFAULT_AVATAR_GRAVITY * _scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT); + velocity += jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } break; case State::InAir: { + const float JUMP_SPEED = _scaleFactor * DEFAULT_AVATAR_JUMP_SPEED; if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) { SET_STATE(State::Ground, "hit ground"); } else { diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index bf84d318d4..585eb7d3ed 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -73,6 +73,7 @@ public: void setStepUpEnabled(bool enabled) { _stepUpEnabled = enabled; } void computeNewVelocity(btScalar dt, btVector3& velocity); void computeNewVelocity(btScalar dt, glm::vec3& velocity); + void setScaleFactor(btScalar scaleFactor) { _scaleFactor = scaleFactor; } // HACK for legacy 'thrust' feature void setLinearAcceleration(const glm::vec3& acceleration) { _linearAcceleration = glmToBullet(acceleration); } @@ -185,7 +186,6 @@ protected: btScalar _gravity { 0.0f }; - btScalar _jumpSpeed; btScalar _followTime; btVector3 _followLinearDisplacement; btQuaternion _followAngularDisplacement; @@ -203,6 +203,8 @@ protected: bool _flyingAllowed { true }; bool _collisionlessAllowed { true }; bool _collisionless { false }; + + btScalar _scaleFactor { 1.0f }; }; #endif // hifi_CharacterController_h diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h new file mode 100644 index 0000000000..a7a80471be --- /dev/null +++ b/libraries/shared/src/AvatarConstants.h @@ -0,0 +1,56 @@ +// +// AvatarConstants.h +// libraries/shared/src +// +// Created by Anthony J. Thibault on Aug 16th 2017 +// Copyright 2017 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_AvatarConstants_h +#define hifi_AvatarConstants_h + +// 50th Percentile Man +const float DEFAULT_AVATAR_HEIGHT = 1.755f; // meters +const float DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD = 0.11f; // meters +const float DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD = 0.185f; // meters +const float DEFAULT_AVATAR_NECK_HEIGHT = DEFAULT_AVATAR_HEIGHT - DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD; +const float DEFAULT_AVATAR_EYE_HEIGHT = DEFAULT_AVATAR_HEIGHT - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; + +// Used when avatar is missing joints... (avatar space) +const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; +const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f }; +const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f }; +const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 }; +const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f }; +const glm::quat DEFAULT_AVATAR_RIGHTARM_ROT { -0.536241f, 0.536241f, -0.460918f, -0.460918f }; +const glm::vec3 DEFAULT_AVATAR_LEFTARM_POS { 0.134795f, 0.396349f, -0.0515881f }; +const glm::quat DEFAULT_AVATAR_LEFTARM_ROT { 0.536257f, 0.536258f, -0.460899f, 0.4609f }; +const glm::vec3 DEFAULT_AVATAR_RIGHTHAND_POS { -0.72768f, 0.396349f, -0.0515779f }; +const glm::quat DEFAULT_AVATAR_RIGHTHAND_ROT { 0.479184f, -0.520013f, 0.522537f, 0.476365f}; +const glm::vec3 DEFAULT_AVATAR_LEFTHAND_POS { 0.727588f, 0.39635f, -0.0515878f }; +const glm::quat DEFAULT_AVATAR_LEFTHAND_ROT { -0.479181f, -0.52001f, 0.52254f, -0.476369f }; +const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.445f, 0.025f }; +const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.32f, 0.02f }; +const glm::quat DEFAULT_AVATAR_SPINE2_ROT { Quaternions::Y_180 }; +const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.0f, 0.0f }; +const glm::quat DEFAULT_AVATAR_HIPS_ROT { Quaternions::Y_180 }; +const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.08f, -0.96f, 0.029f}; +const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.9154590368270874f, -0.005437685176730156f, -0.023744143545627594f }; +const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f }; +const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.9154615998268127f, 0.0053307069465518f, 0.023696165531873703f }; + +const float DEFAULT_AVATAR_MAX_WALKING_SPEED = 2.6f; // meters / second +const float DEFAULT_AVATAR_MAX_FLYING_SPEED = 30.0f; // meters / second + +const float DEFAULT_AVATAR_GRAVITY = -5.0f; // meters / second^2 +const float DEFAULT_AVATAR_JUMP_SPEED = 3.5f; // meters / second +const float DEFAULT_AVATAR_JUMP_HEIGHT = (DEFAULT_AVATAR_JUMP_SPEED * DEFAULT_AVATAR_JUMP_SPEED) / (2.0f * DEFAULT_AVATAR_GRAVITY); // meters + +const float DEFAULT_AVATAR_FALL_HEIGHT = 20.0f; // meters +const float DEFAULT_AVATAR_MIN_HOVER_HEIGHT = 2.5f; // meters + + +#endif // hifi_AvatarConstants_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 5b0095078a..db78144100 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -15,6 +15,7 @@ #include "DependencyManager.h" #include "SharedUtil.h" +#include "StreamUtils.h" #include "SharedLogging.h" const float defaultAACubeSize = 1.0f; @@ -83,8 +84,7 @@ Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const return result; } if (parent) { - Transform parentTransform = parent->getTransform(_parentJointIndex, success, depth + 1); - result = parentTransform.setScale(1.0f); // TODO: scaling + result = parent->getTransform(_parentJointIndex, success, depth + 1); } return result; } @@ -156,7 +156,6 @@ void SpatiallyNestable::setParentJointIndex(quint16 parentJointIndex) { glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success) { - Transform result; QSharedPointer parentFinder = DependencyManager::get(); if (!parentFinder) { success = false; @@ -180,23 +179,17 @@ glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position, if (!success) { return glm::vec3(0.0f); } - parentTransform.setScale(1.0f); // TODO: scale } success = true; - Transform positionTransform; - positionTransform.setTranslation(position); - Transform myWorldTransform; - Transform::mult(myWorldTransform, parentTransform, positionTransform); - myWorldTransform.setTranslation(position); - Transform::inverseMult(result, parentTransform, myWorldTransform); - return result.getTranslation(); + Transform invParentTransform; + parentTransform.evalInverse(invParentTransform); + return invParentTransform.transform(position); } glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success) { - Transform result; QSharedPointer parentFinder = DependencyManager::get(); if (!parentFinder) { success = false; @@ -220,17 +213,11 @@ glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation, if (!success) { return glm::quat(); } - parentTransform.setScale(1.0f); // TODO: scale } success = true; - Transform orientationTransform; - orientationTransform.setRotation(orientation); - Transform myWorldTransform; - Transform::mult(myWorldTransform, parentTransform, orientationTransform); - myWorldTransform.setRotation(orientation); - Transform::inverseMult(result, parentTransform, myWorldTransform); - return result.getRotation(); + glm::quat invParentOrientation = glm::inverse(parentTransform.getRotation()); + return invParentOrientation * orientation; } glm::vec3 SpatiallyNestable::worldToLocalVelocity(const glm::vec3& velocity, const QUuid& parentID, @@ -292,7 +279,6 @@ glm::vec3 SpatiallyNestable::localToWorld(const glm::vec3& position, if (!success) { return glm::vec3(0.0f); } - parentTransform.setScale(1.0f); // TODO: scale } success = true; @@ -634,7 +620,6 @@ const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, i } Transform worldTransform = getTransform(success, depth); - worldTransform.setScale(1.0f); // TODO -- scale; if (!success) { return jointInWorldFrame; } @@ -673,61 +658,51 @@ bool SpatiallyNestable::setTransform(const Transform& transform) { return success; } -glm::vec3 SpatiallyNestable::getScale() const { - // TODO: scale - glm::vec3 result; - _transformLock.withReadLock([&] { - result = _transform.getScale(); - }); +glm::vec3 SpatiallyNestable::getSNScale() const { + bool success; + auto result = getSNScale(success); + #ifdef WANT_DEBUG + if (!success) { + qCDebug(shared) << "Warning -- getScale failed" << getID(); + } + #endif return result; } -glm::vec3 SpatiallyNestable::getScale(int jointIndex) const { - // TODO: scale - return getScale(); +glm::vec3 SpatiallyNestable::getSNScale(bool& success) const { + return getTransform(success).getScale(); } -void SpatiallyNestable::setScale(const glm::vec3& scale) { +glm::vec3 SpatiallyNestable::getSNScale(int jointIndex, bool& success) const { + return getTransform(jointIndex, success).getScale(); +} + +void SpatiallyNestable::setSNScale(const glm::vec3& scale) { + bool success; + setSNScale(scale, success); +} + +void SpatiallyNestable::setSNScale(const glm::vec3& scale, bool& success) { // guard against introducing NaN into the transform if (isNaN(scale)) { - qCDebug(shared) << "SpatiallyNestable::setScale -- scale contains NaN"; + success = false; return; } bool changed = false; - // TODO: scale + Transform parentTransform = getParentTransform(success); + Transform myWorldTransform; _transformLock.withWriteLock([&] { - if (_transform.getScale() != scale) { - _transform.setScale(scale); + Transform::mult(myWorldTransform, parentTransform, _transform); + if (myWorldTransform.getScale() != scale) { changed = true; + myWorldTransform.setScale(scale); + Transform::inverseMult(_transform, parentTransform, myWorldTransform); _scaleChanged = usecTimestampNow(); } }); - if (changed) { - dimensionsChanged(); - } -} - -void SpatiallyNestable::setScale(float value) { - // guard against introducing NaN into the transform - if (value <= 0.0f) { - qCDebug(shared) << "SpatiallyNestable::setScale -- scale is zero or negative value"; - return; - } - - bool changed = false; - // TODO: scale - _transformLock.withWriteLock([&] { - glm::vec3 beforeScale = _transform.getScale(); - _transform.setScale(value); - if (_transform.getScale() != beforeScale) { - changed = true; - _scaleChanged = usecTimestampNow(); - } - }); - - if (changed) { - dimensionsChanged(); + if (success && changed) { + locationChanged(); } } @@ -844,8 +819,7 @@ void SpatiallyNestable::setLocalAngularVelocity(const glm::vec3& angularVelocity }); } -glm::vec3 SpatiallyNestable::getLocalScale() const { - // TODO: scale +glm::vec3 SpatiallyNestable::getLocalSNScale() const { glm::vec3 result; _transformLock.withReadLock([&] { result = _transform.getScale(); @@ -853,7 +827,7 @@ glm::vec3 SpatiallyNestable::getLocalScale() const { return result; } -void SpatiallyNestable::setLocalScale(const glm::vec3& scale) { +void SpatiallyNestable::setLocalSNScale(const glm::vec3& scale) { // guard against introducing NaN into the transform if (isNaN(scale)) { qCDebug(shared) << "SpatiallyNestable::setLocalScale -- scale contains NaN"; @@ -861,7 +835,6 @@ void SpatiallyNestable::setLocalScale(const glm::vec3& scale) { } bool changed = false; - // TODO: scale _transformLock.withWriteLock([&] { if (_transform.getScale() != scale) { _transform.setScale(scale); @@ -905,6 +878,8 @@ const Transform SpatiallyNestable::getAbsoluteJointTransformInObjectFrame(int jo Transform jointTransformInObjectFrame; glm::vec3 position = getAbsoluteJointTranslationInObjectFrame(jointIndex); glm::quat orientation = getAbsoluteJointRotationInObjectFrame(jointIndex); + glm::vec3 scale = getAbsoluteJointScaleInObjectFrame(jointIndex); + jointTransformInObjectFrame.setScale(scale); jointTransformInObjectFrame.setRotation(orientation); jointTransformInObjectFrame.setTranslation(position); return jointTransformInObjectFrame; @@ -1197,3 +1172,13 @@ QString SpatiallyNestable::nestableTypeToString(NestableType nestableType) { return "unknown"; } } + +void SpatiallyNestable::dump(const QString& prefix) const { + qDebug().noquote() << prefix << "id = " << getID(); + qDebug().noquote() << prefix << "transform = " << _transform; + bool success; + SpatiallyNestablePointer parent = getParentPointer(success); + if (success && parent) { + parent->dump(prefix + " "); + } +} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index a1b31d555f..90d2e33016 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -111,14 +111,15 @@ public: virtual AACube getQueryAACube(bool& success) const; virtual AACube getQueryAACube() const; - virtual glm::vec3 getScale() const; - virtual void setScale(const glm::vec3& scale); - virtual void setScale(float value); + virtual glm::vec3 getSNScale() const; + virtual glm::vec3 getSNScale(bool& success) const; + virtual void setSNScale(const glm::vec3& scale); + virtual void setSNScale(const glm::vec3& scale, bool& success); // get world-frame values for a specific joint virtual const Transform getTransform(int jointIndex, bool& success, int depth = 0) const; virtual glm::vec3 getPosition(int jointIndex, bool& success) const; - virtual glm::vec3 getScale(int jointIndex) const; + virtual glm::vec3 getSNScale(int jointIndex, bool& success) const; // object's parent's frame virtual Transform getLocalTransform() const; @@ -136,8 +137,8 @@ public: virtual glm::vec3 getLocalAngularVelocity() const; virtual void setLocalAngularVelocity(const glm::vec3& angularVelocity); - virtual glm::vec3 getLocalScale() const; - virtual void setLocalScale(const glm::vec3& scale); + virtual glm::vec3 getLocalSNScale() const; + virtual void setLocalSNScale(const glm::vec3& scale); QList getChildren() const; bool hasChildren() const; @@ -146,6 +147,7 @@ public: // this object's frame virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const; + virtual glm::vec3 getAbsoluteJointScaleInObjectFrame(int index) const { return glm::vec3(1.0f); } virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { return glm::quat(); } virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { return glm::vec3(); } virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) { return false; } @@ -190,6 +192,8 @@ public: bool tranlationChangedSince(quint64 time) const { return _translationChanged > time; } bool rotationChangedSince(quint64 time) const { return _rotationChanged > time; } + void dump(const QString& prefix = "") const; + protected: const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index c51b3dae4b..3e29c38add 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -150,3 +150,10 @@ QJsonObject Transform::toJson(const Transform& transform) { } return result; } + +QDebug& operator<<(QDebug& debug, const Transform& transform) { + debug << "Transform, trans = (" << transform._translation.x << transform._translation.y << transform._translation.z << "), rot = (" + << transform._rotation.x << transform._rotation.y << transform._rotation.z << transform._rotation.w << "), scale = (" + << transform._scale.x << transform._scale.y << transform._scale.z << ")"; + return debug; +} diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 38d47695f7..316fcb2f04 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -38,6 +38,7 @@ inline bool isValidScale(float scale) { class Transform { public: + friend QDebug& operator<<(QDebug& debug, const Transform& transform); using Pointer = std::shared_ptr; typedef glm::mat4 Mat4; typedef glm::mat3 Mat3; @@ -170,7 +171,6 @@ protected: }; typedef std::bitset Flags; - // TRS Quat _rotation; Vec3 _scale; @@ -202,6 +202,8 @@ protected: Mat4& getCachedMatrix(Mat4& result) const; }; +QDebug& operator<<(QDebug& debug, const Transform& transform); + inline Transform& Transform::setIdentity() { _translation = Vec3(0.0f); _rotation = Quat(1.0f, 0.0f, 0.0f, 0.0f); diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index adff219e0f..8ab03b60d0 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -425,6 +425,9 @@ void TabletProxy::gotoMenuScreen(const QString& submenu) { emit screenChanged(QVariant("Menu"), QVariant(VRMENU_SOURCE_URL)); _currentPathLoaded = VRMENU_SOURCE_URL; QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); + if (_toolbarMode && _desktopWindow) { + QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); + } } } @@ -444,6 +447,9 @@ void TabletProxy::loadQMLOnTop(const QVariant& path) { if (root) { QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, path)); QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); + if (_toolbarMode && _desktopWindow) { + QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); + } } else { qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } @@ -470,9 +476,9 @@ void TabletProxy::returnToPreviousApp() { } } -void TabletProxy::loadQMLSource(const QVariant& path) { +void TabletProxy::loadQMLSource(const QVariant& path, bool resizable) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadQMLSource", Q_ARG(QVariant, path)); + QMetaObject::invokeMethod(this, "loadQMLSource", Q_ARG(QVariant, path), Q_ARG(bool, resizable)); return; } @@ -492,6 +498,10 @@ void TabletProxy::loadQMLSource(const QVariant& path) { } _currentPathLoaded = path; QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); + if (_toolbarMode && _desktopWindow) { + QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(resizable))); + } + } else { qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } @@ -523,6 +533,9 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { } else { loadQMLSource(path); } + if (_toolbarMode && _desktopWindow) { + QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); + } } else { qCDebug(uiLogging) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } @@ -599,6 +612,9 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJ if (root) { QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); + if (_toolbarMode && _desktopWindow) { + QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); + } QMetaObject::invokeMethod(root, "loadWebOnTop", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectJavaScriptUrl))); } _state = State::Web; @@ -625,6 +641,9 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS QMetaObject::invokeMethod(root, "loadWebBase"); } QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); + if (_toolbarMode && _desktopWindow) { + QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); + } QMetaObject::invokeMethod(root, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl))); } _state = State::Web; diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 822bae839e..d3590ec62e 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -124,7 +124,7 @@ public: Q_INVOKABLE void gotoWebScreen(const QString& url); Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase = false); - Q_INVOKABLE void loadQMLSource(const QVariant& path); + Q_INVOKABLE void loadQMLSource(const QVariant& path, bool resizable = false); // FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success); // it should be initialized internally so it cannot fail Q_INVOKABLE bool pushOntoStack(const QVariant& path); diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 69ad8a5710..7e7fcc0db3 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -60,6 +60,36 @@ bool OculusBaseDisplayPlugin::isSupported() const { return oculusAvailable(); } +glm::mat4 OculusBaseDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const { + if (_session) { + ViewFrustum baseFrustum; + baseFrustum.setProjection(baseProjection); + float baseNearClip = baseFrustum.getNearClip(); + float baseFarClip = baseFrustum.getFarClip(); + ovrEyeType ovrEye = (eye == Left) ? ovrEye_Left : ovrEye_Right; + ovrFovPort fovPort = _hmdDesc.DefaultEyeFov[eye]; + ovrEyeRenderDesc& erd = ovr_GetRenderDesc(_session, ovrEye, fovPort); + ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(erd.Fov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL); + return toGlm(ovrPerspectiveProjection); + } else { + return baseProjection; + } +} + +glm::mat4 OculusBaseDisplayPlugin::getCullingProjection(const glm::mat4& baseProjection) const { + if (_session) { + ViewFrustum baseFrustum; + baseFrustum.setProjection(baseProjection); + float baseNearClip = baseFrustum.getNearClip(); + float baseFarClip = baseFrustum.getFarClip(); + auto combinedFov = _eyeFovs[0]; + combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan); + return toGlm(ovrMatrix4f_Projection(combinedFov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL)); + } else { + return baseProjection; + } +} + // DLL based display plugins MUST initialize GLEW inside the DLL code. void OculusBaseDisplayPlugin::customizeContext() { glewExperimental = true; diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index 5230b11681..b3b99c0ad1 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -19,6 +19,9 @@ public: ~OculusBaseDisplayPlugin(); bool isSupported() const override; + glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override; + glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override; + bool hasAsyncReprojection() const override { return true; } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 771ba8a3d8..96ad19f46b 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -357,6 +357,32 @@ bool OpenVrDisplayPlugin::isSupported() const { return openVrSupported(); } +glm::mat4 OpenVrDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const { + if (_system) { + ViewFrustum baseFrustum; + baseFrustum.setProjection(baseProjection); + float baseNearClip = baseFrustum.getNearClip(); + float baseFarClip = baseFrustum.getFarClip(); + vr::EVREye openVrEye = (eye == Left) ? vr::Eye_Left : vr::Eye_Right; + return toGlm(_system->GetProjectionMatrix(openVrEye, baseNearClip, baseFarClip)); + } else { + return baseProjection; + } +} + +glm::mat4 OpenVrDisplayPlugin::getCullingProjection(const glm::mat4& baseProjection) const { + if (_system) { + ViewFrustum baseFrustum; + baseFrustum.setProjection(baseProjection); + float baseNearClip = baseFrustum.getNearClip(); + float baseFarClip = baseFrustum.getFarClip(); + // FIXME Calculate the proper combined projection by using GetProjectionRaw values from both eyes + return toGlm(_system->GetProjectionMatrix((vr::EVREye)0, baseNearClip, baseFarClip)); + } else { + return baseProjection; + } +} + float OpenVrDisplayPlugin::getTargetFrameRate() const { if (forceInterleavedReprojection && !_asyncReprojectionActive) { return TARGET_RATE_OpenVr / 2.0f; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index a1bbed8754..6aea6ef575 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -38,6 +38,9 @@ public: bool isSupported() const override; const QString getName() const override { return NAME; } + glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override; + glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override; + void init() override; float getTargetFrameRate() const override; diff --git a/scripts/system/away.js b/scripts/system/away.js index f98408376d..18eb5fa946 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -14,6 +14,8 @@ // Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key. // See MAIN CONTROL, below, for what "paused" actually does. +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ + (function() { // BEGIN LOCAL_SCOPE var BASIC_TIMER_INTERVAL = 50; // 50ms = 20hz @@ -28,18 +30,22 @@ var OVERLAY_DATA = { }; var AVATAR_MOVE_FOR_ACTIVE_DISTANCE = 0.8; // meters -- no longer away if avatar moves this far while away -var lastOverlayPosition = { x: 0, y: 0, z: 0}; +var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}"; +var CAMERA_MATRIX = -7; + var OVERLAY_DATA_HMD = { - position: lastOverlayPosition, + localPosition: {x: 0, y: 0, z: -1 * MyAvatar.sensorToWorldScale}, + localRotation: {x: 0, y: 0, z: 0, w: 1}, width: OVERLAY_WIDTH, height: OVERLAY_HEIGHT, url: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png", color: {red: 255, green: 255, blue: 255}, alpha: 1, - scale: 2, + scale: 2 * MyAvatar.sensorToWorldScale, emissive: true, - isFacingAvatar: true, - drawInFront: true + drawInFront: true, + parentID: AVATAR_SELF_ID, + parentJointIndex: CAMERA_MATRIX }; var AWAY_INTRO = { @@ -83,25 +89,11 @@ function stopAwayAnimation() { var overlay = Overlays.addOverlay("image", OVERLAY_DATA); var overlayHMD = Overlays.addOverlay("image3d", OVERLAY_DATA_HMD); -function moveCloserToCamera(positionAtHUD) { - // we don't actually want to render at the slerped look at... instead, we want to render - // slightly closer to the camera than that. - var MOVE_CLOSER_TO_CAMERA_BY = -0.25; - var cameraForward = Quat.getForward(Camera.orientation); - var closerToCamera = Vec3.multiply(cameraForward, MOVE_CLOSER_TO_CAMERA_BY); // slightly closer to camera - var slightlyCloserPosition = Vec3.sum(positionAtHUD, closerToCamera); - - return slightlyCloserPosition; -} - function showOverlay() { if (HMD.active) { // make sure desktop version is hidden Overlays.editOverlay(overlay, { visible: false }); - - lastOverlayPosition = HMD.getHUDLookAtPosition3D(); - var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition); - Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon }); + Overlays.editOverlay(overlayHMD, { visible: true }); } else { // make sure HMD is hidden Overlays.editOverlay(overlayHMD, { visible: false }); @@ -110,9 +102,11 @@ function showOverlay() { var screen = Controller.getViewportDimensions(); // keep the overlay it's natural size and always center it... - Overlays.editOverlay(overlay, { visible: true, - x: ((screen.x - OVERLAY_WIDTH) / 2), - y: ((screen.y - OVERLAY_HEIGHT) / 2) }); + Overlays.editOverlay(overlay, { + visible: true, + x: ((screen.x - OVERLAY_WIDTH) / 2), + y: ((screen.y - OVERLAY_HEIGHT) / 2) + }); } } @@ -132,16 +126,10 @@ function maybeMoveOverlay() { } if (HMD.active) { - // Note: instead of moving it directly to the lookAt, we will move it slightly toward the - // new look at. This will result in a more subtle slerp toward the look at and reduce jerkiness - var EASE_BY_RATIO = 0.1; - var lookAt = HMD.getHUDLookAtPosition3D(); - var lookAtChange = Vec3.subtract(lookAt, lastOverlayPosition); - var halfWayBetweenOldAndLookAt = Vec3.multiply(lookAtChange, EASE_BY_RATIO); - var newOverlayPosition = Vec3.sum(lastOverlayPosition, halfWayBetweenOldAndLookAt); - lastOverlayPosition = newOverlayPosition; - var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition); - Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon }); + + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + var localPosition = {x: 0, y: 0, z: -1 * sensorScaleFactor}; + Overlays.editOverlay(overlayHMD, { visible: true, localPosition: localPosition, scale: 2 * sensorScaleFactor }); // make sure desktop version is hidden Overlays.editOverlay(overlay, { visible: false }); @@ -191,8 +179,8 @@ function goActive() { MyAvatar.isAway = false; } -MyAvatar.wentAway.connect(setAwayProperties) -MyAvatar.wentActive.connect(setActiveProperties) +MyAvatar.wentAway.connect(setAwayProperties); +MyAvatar.wentActive.connect(setActiveProperties); function setAwayProperties() { isAway = true; @@ -254,7 +242,7 @@ function maybeGoActive(event) { if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it) return; } - if (!isAway && (event.text == 'ESC')) { + if (!isAway && (event.text === 'ESC')) { goAway(); } else { goActive(); @@ -286,7 +274,7 @@ function maybeGoAway() { } // If you've removed your HMD from your head, and we can detect it, we will also go away... - if (HMD.mounted != wasHmdMounted) { + if (HMD.mounted !== wasHmdMounted) { wasHmdMounted = HMD.mounted; print("HMD mounted changed..."); @@ -317,7 +305,7 @@ var handleMessage = function(channel, message, sender) { print("away.js | Got message on Hifi-Away-Enable: ", message); setEnabled(message === 'enable'); } -} +}; Messages.subscribe(CHANNEL_AWAY_ENABLE); Messages.messageReceived.connect(handleMessage); diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index c2a2f7af40..c3d240f9a5 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -27,8 +27,8 @@ url: Script.resolvePath("assets/models/Bubble-v14.fbx"), // If you'd like to change the model, modify this line (and the dimensions below) dimensions: { x: 1.0, y: 0.75, z: 1.0 }, position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), - scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 }, + rotation: Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({x: 0.0, y: 180.0, z: 0.0})), + scale: { x: 2 * MyAvatar.sensorToWorldScale, y: MyAvatar.scale * 0.5 + 0.2 * MyAvatar.sensorToWorldScale, z: 2 * MyAvatar.sensorToWorldScale }, visible: false, ignoreRayIntersection: true }); @@ -62,9 +62,17 @@ } Overlays.editOverlay(bubbleOverlay, { - position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), - scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 }, + position: { + x: MyAvatar.position.x, + y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, + z: MyAvatar.position.z + }, + rotation: Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({x: 0.0, y: 180.0, z: 0.0})), + scale: { + x: 2 * MyAvatar.sensorToWorldScale, + y: MyAvatar.scale * 0.5 + 0.2 * MyAvatar.sensorToWorldScale, + z: 2 * MyAvatar.sensorToWorldScale + }, visible: true }); bubbleOverlayTimestamp = Date.now(); @@ -105,11 +113,11 @@ y: (-((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), + rotation: Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({x: 0.0, y: 180.0, z: 0.0})), scale: { - x: 2, - y: ((1 - ((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 0.5 + 0.5), - z: 2 + x: 2 * MyAvatar.sensorToWorldScale, + y: ((1 - ((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 0.5 + 0.2 * MyAvatar.sensorToWorldScale), + z: 2 * MyAvatar.sensorToWorldScale } }); } else { @@ -120,11 +128,11 @@ y: MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z }, - rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll), + rotation: Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({x: 0.0, y: 180.0, z: 0.0})), scale: { - x: 2, - y: MyAvatar.scale * 0.5 + 0.5, - z: 2 + x: 2 * MyAvatar.sensorToWorldScale, + y: MyAvatar.scale * 0.5 + 0.2 * MyAvatar.sensorToWorldScale, + z: 2 * MyAvatar.sensorToWorldScale } }); } diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 990f156ba8..63657e9b6f 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -150,6 +150,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); if (PROFILE) { Script.beginProfileRange("dispatch.pre"); } + var sensorScaleFactor = MyAvatar.sensorToWorldScale; var deltaTime = _this.updateTimings(); _this.setIgnoreTablet(); @@ -196,7 +197,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var h; for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { if (controllerLocations[h].valid) { - var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS); + var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS * sensorScaleFactor); nearbyOverlays.sort(function (a, b) { var aPosition = Overlays.getProperty(a, "position"); var aDistance = Vec3.distance(aPosition, controllerLocations[h].position); @@ -216,7 +217,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { if (controllerLocations[h].valid) { var controllerPosition = controllerLocations[h].position; - var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MAX_RADIUS); + var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MAX_RADIUS * sensorScaleFactor); for (var j = 0; j < nearbyEntityIDs.length; j++) { var entityID = nearbyEntityIDs[j]; var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); @@ -249,7 +250,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); if (rayPicks[h].type === RayPick.INTERSECTED_ENTITY) { // XXX check to make sure this one isn't already in nearbyEntityProperties? - if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS) { + if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS * sensorScaleFactor) { var nearEntityID = rayPicks[h].objectID; var nearbyProps = Entities.getEntityProperties(nearEntityID, DISPATCHER_PROPERTIES); nearbyProps.id = nearEntityID; @@ -368,28 +369,28 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, enabled: true, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) + posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true) }); this.leftControllerHudRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_HUD, enabled: true, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) + posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true) }); this.rightControllerRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_RIGHTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, enabled: true, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) + posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true) }); this.rightControllerHudRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_RIGHTHAND", filter: RayPick.PICK_HUD, enabled: true, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) + posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true) }); this.handleHandMessage = function(channel, message, sender) { diff --git a/scripts/system/controllers/controllerDisplay.js b/scripts/system/controllers/controllerDisplay.js index 980829e0a3..af8cfa74f4 100644 --- a/scripts/system/controllers/controllerDisplay.js +++ b/scripts/system/controllers/controllerDisplay.js @@ -59,7 +59,8 @@ createControllerDisplay = function(config) { }, setPartVisible: function(partName, visible) { - return; + // Disabled + /* if (partName in this.partOverlays) { for (var i = 0; i < this.partOverlays[partName].length; ++i) { Overlays.editOverlay(this.partOverlays[partName][i], { @@ -67,6 +68,7 @@ createControllerDisplay = function(config) { }); } } + */ }, setLayerForPart: function(partName, layerName) { @@ -85,12 +87,45 @@ createControllerDisplay = function(config) { } } } + }, + + resize: function(sensorScaleFactor) { + if (this.overlays.length >= 0) { + var controller = config.controllers[0]; + var position = controller.position; + + // first overlay is main body. + var overlayID = this.overlays[0]; + var localPosition = Vec3.multiply(sensorScaleFactor, Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position)); + var dimensions = Vec3.multiply(sensorScaleFactor, controller.dimensions); + + Overlays.editOverlay(overlayID, { + dimensions: dimensions, + localPosition: localPosition + }); + + if (controller.parts) { + var i = 1; + for (var partName in controller.parts) { + overlayID = this.overlays[i++]; + var part = controller.parts[partName]; + var partPosition = Vec3.multiply(sensorScaleFactor, Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition))); + var partDimensions = Vec3.multiply(sensorScaleFactor, part.naturalDimensions); + Overlays.editOverlay(overlayID, { + dimensions: partDimensions, + localPosition: partPosition + }); + } + } + } } }; + var mapping = Controller.newMapping(controllerDisplay.mappingName); for (var i = 0; i < config.controllers.length; ++i) { var controller = config.controllers[i]; var position = controller.position; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; if (controller.naturalPosition) { position = Vec3.sum(Vec3.multiplyQbyV(controller.rotation, controller.naturalPosition), position); @@ -98,9 +133,9 @@ createControllerDisplay = function(config) { var overlayID = Overlays.addOverlay("model", { url: controller.modelURL, - dimensions: controller.dimensions, + dimensions: Vec3.multiply(sensorScaleFactor, controller.dimensions), localRotation: controller.rotation, - localPosition: position, + localPosition: Vec3.multiply(sensorScaleFactor, position), parentID: PARENT_ID, parentJointIndex: controller.jointIndex, ignoreRayIntersection: true @@ -176,8 +211,8 @@ createControllerDisplay = function(config) { }); } else if (part.type === "joystick") { (function(controller, overlayID, part) { - const xInput = resolveHardware(part.xInput); - const yInput = resolveHardware(part.yInput); + var xInput = resolveHardware(part.xInput); + var yInput = resolveHardware(part.yInput); var xvalue = 0; var yvalue = 0; @@ -190,21 +225,20 @@ createControllerDisplay = function(config) { offset = Vec3.multiplyQbyV(rotation, part.originOffset); offset = Vec3.subtract(part.originOffset, offset); } - + var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition))); - var partRotation = Quat.multiply(controller.rotation, rotation) + var partRotation = Quat.multiply(controller.rotation, rotation); return { position: partPosition, rotation: partRotation - } + }; } mapping.from([xInput]).peek().to(function(value) { xvalue = value; - //print(overlayID, xvalue.toFixed(3), yvalue.toFixed(3)); var posRot = calculatePositionAndRotation(xvalue, yvalue); Overlays.editOverlay(overlayID, { localPosition: posRot.position, @@ -224,10 +258,9 @@ createControllerDisplay = function(config) { } else if (part.type === "linear") { (function(controller, overlayID, part) { - const input = resolveHardware(part.input); + var input = resolveHardware(part.input); mapping.from([input]).peek().to(function(value) { - //print(value); var axis = Vec3.multiplyQbyV(controller.rotation, part.axis); var offset = Vec3.multiply(part.maxTranslation * value, axis); @@ -256,6 +289,7 @@ createControllerDisplay = function(config) { } } Controller.enableMapping(controllerDisplay.mappingName); + controllerDisplay.resize(MyAvatar.sensorToWorldScale); return controllerDisplay; }; diff --git a/scripts/system/controllers/controllerDisplayManager.js b/scripts/system/controllers/controllerDisplayManager.js index 56becfc453..e3fd74e05b 100644 --- a/scripts/system/controllers/controllerDisplayManager.js +++ b/scripts/system/controllers/controllerDisplayManager.js @@ -56,7 +56,6 @@ ControllerDisplayManager = function() { } if (leftConfig !== null && rightConfig !== null) { - print("Loading controllers"); if (controllerLeft === null) { controllerLeft = createControllerDisplay(leftConfig); controllerLeft.setVisible(true); @@ -70,6 +69,7 @@ ControllerDisplayManager = function() { Script.clearInterval(controllerCheckerIntervalID); controllerCheckerIntervalID = null; } + } else { self.deleteControllerDisplays(); if (!controllerCheckerIntervalID) { @@ -86,6 +86,15 @@ ControllerDisplayManager = function() { } } + function resizeControllers(sensorScaleFactor) { + if (controllerLeft) { + controllerLeft.resize(sensorScaleFactor); + } + if (controllerRight) { + controllerRight.resize(sensorScaleFactor); + } + }; + var handleMessages = function(channel, message, sender) { var i, data, name, visible; if (!controllerLeft && !controllerRight) { @@ -171,6 +180,7 @@ ControllerDisplayManager = function() { HMD.displayModeChanged.connect(updateControllers); HMD.shouldShowHandControllersChanged.connect(updateControllers); + MyAvatar.sensorToWorldScaleChanged.connect(resizeControllers); updateControllers(); }; diff --git a/scripts/system/controllers/controllerModules/disableOtherModule.js b/scripts/system/controllers/controllerModules/disableOtherModule.js index d6079ffafb..92784ec2ed 100644 --- a/scripts/system/controllers/controllerModules/disableOtherModule.js +++ b/scripts/system/controllers/controllerModules/disableOtherModule.js @@ -1,15 +1,13 @@ "use strict"; -// nearTrigger.js +// disableOtherModule.js // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - enableDispatcherModule, disableDispatcherModule, getGrabbableData, Vec3, - TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS, - getEnabledModuleByName +/* global Script, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, + makeDispatcherModuleParameters, makeRunningValues, getEnabledModuleByName, Messages */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -20,7 +18,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.disableModules = false; this.parameters = makeDispatcherModuleParameters( 90, - this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], + this.hand === RIGHT_HAND ? + ["rightHand", "rightHandEquip", "rightHandTrigger"] : + ["leftHand", "leftHandEquip", "leftHandTrigger"], [], 100); @@ -37,11 +37,11 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); if (teleportModule) { var ready = teleportModule.isReady(controllerData); - if (ready) { + if (ready.active) { return makeRunningValues(false, [], []); } } - if (!this.disablemodules) { + if (!this.disableModules) { return makeRunningValues(false, [], []); } return makeRunningValues(true, [], []); @@ -61,7 +61,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } if (message === 'right') { rightDisableModules.disableModules = true; - } if (message === 'both' || message === 'none') { if (message === 'both') { @@ -75,7 +74,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } } }; - + Messages.subscribe('Hifi-Hand-Disabler'); this.cleanup = function() { disableDispatcherModule("LeftDisableModules"); diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index fa1321b168..fe868493f4 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -602,11 +602,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; this.isTargetIDValid = function() { - var entityProperties = Entities.getEntityProperties(this.targetEntityID); - for (var propertry in entityProperties) { - return true; - } - return false; + var entityProperties = Entities.getEntityProperties(this.targetEntityID, ["type"]); + return "type" in entityProperties; }; this.isReady = function (controllerData, deltaTime) { diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 80718bc68d..eb73b0f908 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -13,7 +13,7 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays */ @@ -236,10 +236,10 @@ Script.include("/~/system/libraries/controllers.js"); this.actionID = null; } - // XXX - // if (this.actionID !== null) { - // this.callEntityMethodOnGrabbed("startDistanceGrab"); - // } + if (this.actionID !== null) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "startDistanceGrab", args); + } Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); this.previousRoomControllerPosition = roomControllerPosition; @@ -271,8 +271,8 @@ Script.include("/~/system/libraries/controllers.js"); var handMoved = Vec3.multiply(worldHandDelta, radius); this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved); - // XXX - // this.callEntityMethodOnGrabbed("continueDistantGrab"); + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "continueDistanceGrab", args); // Update radialVelocity var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime); @@ -335,6 +335,10 @@ Script.include("/~/system/libraries/controllers.js"); this.distanceHolding = false; this.distanceRotating = false; Entities.deleteAction(this.grabbedThingID, this.actionID); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "releaseGrab", args); + this.actionID = null; this.grabbedThingID = null; }; @@ -343,7 +347,8 @@ Script.include("/~/system/libraries/controllers.js"); var intersection = controllerData.rayPicks[this.hand]; var entityProperty = Entities.getEntityProperties(intersection.objectID); var entityType = entityProperty.type; - if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") || intersection.type === RayPick.INTERSECTED_OVERLAY) { + if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") || + intersection.type === RayPick.INTERSECTED_OVERLAY) { return true; } return false; @@ -354,7 +359,8 @@ Script.include("/~/system/libraries/controllers.js"); this.distanceHolding = false; var worldControllerRotation = getControllerWorldLocation(this.handToController(), true).orientation; - var controllerRotationDelta = Quat.multiply(worldControllerRotation, Quat.inverse(this.previousWorldControllerRotation)); + var controllerRotationDelta = + Quat.multiply(worldControllerRotation, Quat.inverse(this.previousWorldControllerRotation)); // Rotate entity by twice the delta rotation. controllerRotationDelta = Quat.multiply(controllerRotationDelta, controllerRotationDelta); @@ -426,7 +432,8 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.notPointingAtEntity(controllerData) || this.isPointingAtUI(controllerData)) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || + this.notPointingAtEntity(controllerData) || this.isPointingAtUI(controllerData)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); @@ -471,6 +478,7 @@ Script.include("/~/system/libraries/controllers.js"); for (var j = 0; j < nearGrabReadiness.length; j++) { if (nearGrabReadiness[j].active) { this.laserPointerOff(); + this.endNearGrabAction(); return makeRunningValues(false, [], []); } } @@ -555,7 +563,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: (this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, maxDistance: PICK_MAX_DISTANCE, - posOffset: getGrabPointSphereOffset(this.handToController()), + posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, defaultRenderStates: defaultRenderStates diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index 38152aac91..a683044e6e 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -6,14 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, - getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, - enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, - makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, +/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, MyAvatar, getGrabPointSphereOffset, + makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, getGrabbableData - + AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -202,7 +198,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: (this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, maxDistance: PICK_MAX_DISTANCE, - posOffset: getGrabPointSphereOffset(this.handToController()), + posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, defaultRenderStates: defaultRenderStates diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 916487277a..cbe64b1870 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -5,13 +5,12 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, - Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, - AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, - COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, - isInEditMode +/* jslint bitwise: true */ + +/* global Script, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, makeRunningValues, + Messages, makeDispatcherModuleParameters, AVATAR_SELF_ID, HMD, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, + getEnabledModuleByName, PICK_MAX_DISTANCE, isInEditMode, LaserPointers, RayPick */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -239,7 +238,7 @@ Script.include("/~/system/libraries/utils.js"); joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, maxDistance: PICK_MAX_DISTANCE, - posOffset: getGrabPointSphereOffset(this.handToController()), + posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, defaultRenderStates: defaultRenderStates diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 399388d614..bd7a64572a 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -111,6 +111,9 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); grabbedEntity: this.targetEntityID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "startNearGrab", args); }; // this is for when the action is going to time-out @@ -147,10 +150,10 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.getTargetProps = function (controllerData) { // nearbyEntityProperties is already sorted by distance from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; - var handPosition = controllerData.controllerLocations[this.hand].position; - if (props.distance > NEAR_GRAB_RADIUS) { + if (props.distance > NEAR_GRAB_RADIUS * sensorScaleFactor) { break; } if (entityIsGrabbable(props) || entityIsCloneable(props)) { @@ -173,7 +176,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.targetEntityID = null; var targetProps = this.getTargetProps(controllerData); - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && + controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { return makeRunningValues(false, [], []); } @@ -192,7 +196,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.run = function (controllerData) { if (this.actionID) { - if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { + if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && + controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { this.endNearGrabAction(); this.hapticTargetID = null; return makeRunningValues(false, [], []); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 6cd52fef07..e08b61dbd5 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -6,11 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, - getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, - propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, - makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, - findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, + Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -147,11 +146,12 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.getTargetProps = function (controllerData) { // nearbyEntityProperties is already sorted by length from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(props.position, handPosition); - if (distance > NEAR_GRAB_RADIUS) { + if (distance > NEAR_GRAB_RADIUS * sensorScaleFactor) { continue; } if (entityIsGrabbable(props)) { @@ -176,7 +176,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.grabbing = false; var targetProps = this.getTargetProps(controllerData); - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && + controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { return makeRunningValues(false, [], []); } @@ -195,7 +196,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.run = function (controllerData, deltaTime) { if (this.grabbing) { - if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { + if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && + controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { this.endNearParentingGrabEntity(); this.hapticTargetID = null; return makeRunningValues(false, [], []); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 54257ea6af..4ad17e6de9 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -6,14 +6,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, - getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, - Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - makeDispatcherModuleParameters, Overlays, makeRunningValues +/* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + makeDispatcherModuleParameters, Overlays, makeRunningValues, Vec3, resizeTablet, getTabletWidthFromSettings, + NEAR_GRAB_RADIUS */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); -var GRAB_RADIUS = 0.35; +Script.include("/~/system/libraries/utils.js"); (function() { @@ -120,6 +120,11 @@ var GRAB_RADIUS = 0.35; } Overlays.editOverlay(this.grabbedThingID, reparentProps); + // resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix + if (this.grabbedThingID === HMD.tabletID) { + resizeTablet(getTabletWidthFromSettings(), reparentProps.parentJointIndex); + } + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'grab', grabbedEntity: this.grabbedThingID, @@ -140,17 +145,23 @@ var GRAB_RADIUS = 0.35; parentID: this.previousParentID[this.grabbedThingID], parentJointIndex: this.previousParentJointIndex[this.grabbedThingID] }); + + // resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix + if (this.grabbedThingID === HMD.tabletID) { + resizeTablet(getTabletWidthFromSettings(), this.previousParentJointIndex[this.grabbedThingID]); + } } this.grabbedThingID = null; }; this.getTargetID = function(overlays, controllerData) { + var sensorScaleFactor = MyAvatar.sensorToWorldScale; for (var i = 0; i < overlays.length; i++) { var overlayPosition = Overlays.getProperty(overlays[i], "position"); var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(overlayPosition, handPosition); - if (distance <= GRAB_RADIUS) { + if (distance <= NEAR_GRAB_RADIUS * sensorScaleFactor) { return overlays[i]; } } diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 03fc7f8f64..edea1f993d 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -6,9 +6,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - enableDispatcherModule, disableDispatcherModule, getGrabbableData, Vec3, - TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS +/* global Script, Entities, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, getGrabbableData, + Vec3, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -37,11 +36,12 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.getTargetProps = function (controllerData) { // nearbyEntityProperties is already sorted by length from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(props.position, handPosition); - if (distance > NEAR_GRAB_RADIUS) { + if (distance > NEAR_GRAB_RADIUS * sensorScaleFactor) { continue; } if (entityWantsNearTrigger(props)) { @@ -63,7 +63,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.endNearTrigger = function (controllerData) { var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.targetEntityID, "endNearTrigger", args); + Entities.callEntityMethod(this.targetEntityID, "stopNearTrigger", args); }; this.isReady = function (controllerData) { diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 2c950fd4df..218122e876 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -5,13 +5,11 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, - Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, - AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, - COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, - DISPATCHER_PROPERTIES +/* global Script, Entities, Controller, RIGHT_HAND, LEFT_HAND, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + makeRunningValues, Messages, Quat, Vec3, makeDispatcherModuleParameters, Overlays, ZERO_VEC, AVATAR_SELF_ID, HMD, + INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, + TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, LaserPointers, RayPick, ContextOverlay */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -528,7 +526,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_OVERLAYS, maxDistance: PICK_MAX_DISTANCE, - posOffset: getGrabPointSphereOffset(this.handToController()), + posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, defaultRenderStates: defaultRenderStates diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index b98e26b1da..05804c967b 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -7,9 +7,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, - Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, - setGrabCommunications, Menu, HMD, isInEditMode, AvatarList */ +/* global Script, Vec3, MyAvatar, RIGHT_HAND */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function () { @@ -37,7 +35,8 @@ }; this.triggersPressed = function(controllerData) { - if (controllerData.triggerClicks[this.hand] && controllerData.secondaryValues[this.hand] > dispatcherUtils.BUMPER_ON_VALUE) { + if (controllerData.triggerClicks[this.hand] && + controllerData.secondaryValues[this.hand] > dispatcherUtils.BUMPER_ON_VALUE) { return true; } return false; @@ -58,7 +57,8 @@ var otherModule = this.getOtherModule(); if (this.triggersPressed(controllerData) && otherModule.triggersPressed(controllerData)) { if (this.hand === dispatcherUtils.RIGHT_HAND) { - var scalingCurrentDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, + var scalingCurrentDistance = + Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, controllerData.controllerLocations[this.otherHand()].position)); var newAvatarScale = (scalingCurrentDistance / this.scalingStartDistance) * this.scalingStartAvatarScale; diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 0d4f4ff78a..230038adb5 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -8,7 +8,8 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, - AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, + getEnabledModuleByName */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -356,7 +357,7 @@ Script.include("/~/system/libraries/controllers.js"); this.stylusTip = getControllerWorldLocation(this.handToController(), true); // translate tip forward according to constant. - var TIP_OFFSET = {x: 0, y: WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, z: 0}; + var TIP_OFFSET = Vec3.multiply(MyAvatar.sensorToWorldScale, {x: 0, y: WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, z: 0}); this.stylusTip.position = Vec3.sum(this.stylusTip.position, Vec3.multiplyQbyV(this.stylusTip.orientation, TIP_OFFSET)); } @@ -380,17 +381,17 @@ Script.include("/~/system/libraries/controllers.js"); return; } + var X_ROT_NEG_90 = { x: -0.70710678, y: 0, z: 0, w: 0.70710678 }; + var modelOrientation = Quat.multiply(this.stylusTip.orientation, X_ROT_NEG_90); + var modelPositionOffset = Vec3.multiplyQbyV(modelOrientation, { x: 0, y: 0, z: MyAvatar.sensorToWorldScale * -WEB_STYLUS_LENGTH / 2 }); + var stylusProperties = { name: "stylus", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", loadPriority: 10.0, - localPosition: Vec3.sum({ - x: 0.0, - y: WEB_TOUCH_Y_OFFSET, - z: 0.0 - }, getGrabPointSphereOffset(this.handToController())), - localRotation: Quat.fromVec3Degrees({ x: -90, y: 0, z: 0 }), - dimensions: { x: 0.01, y: 0.01, z: WEB_STYLUS_LENGTH }, + position: Vec3.sum(this.stylusTip.position, modelPositionOffset), + rotation: modelOrientation, + dimensions: Vec3.multiply(MyAvatar.sensorToWorldScale, { x: 0.01, y: 0.01, z: WEB_STYLUS_LENGTH }), solid: true, visible: true, ignoreRayIntersection: true, @@ -527,9 +528,11 @@ Script.include("/~/system/libraries/controllers.js"); hysteresisOffset = 0.05; } - this.isNearStylusTarget = isNearStylusTarget(stylusTargets, EDGE_BORDER + hysteresisOffset, - TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset, - WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset); + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + this.isNearStylusTarget = isNearStylusTarget(stylusTargets, + (EDGE_BORDER + hysteresisOffset) * sensorScaleFactor, + (TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor, + (WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor); if (this.isNearStylusTarget) { if (!this.useFingerInsteadOfStylus) { @@ -544,8 +547,12 @@ Script.include("/~/system/libraries/controllers.js"); var nearestStylusTarget = calculateNearestStylusTarget(stylusTargets); - if (nearestStylusTarget && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && - nearestStylusTarget.distance < TABLET_MAX_HOVER_DISTANCE) { + var SCALED_TABLET_MIN_TOUCH_DISTANCE = TABLET_MIN_TOUCH_DISTANCE * sensorScaleFactor; + var SCALED_TABLET_MAX_TOUCH_DISTANCE = TABLET_MAX_TOUCH_DISTANCE * sensorScaleFactor; + var SCALED_TABLET_MAX_HOVER_DISTANCE = TABLET_MAX_HOVER_DISTANCE * sensorScaleFactor; + + if (nearestStylusTarget && nearestStylusTarget.distance > SCALED_TABLET_MIN_TOUCH_DISTANCE && + nearestStylusTarget.distance < SCALED_TABLET_MAX_HOVER_DISTANCE) { this.requestTouchFocus(nearestStylusTarget); @@ -559,8 +566,8 @@ Script.include("/~/system/libraries/controllers.js"); // filter out presses when tip is moving away from tablet. // ensure that stylus is within bounding box by checking normalizedPosition - if (nearestStylusTarget.valid && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && - nearestStylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE && + if (nearestStylusTarget.valid && nearestStylusTarget.distance > SCALED_TABLET_MIN_TOUCH_DISTANCE && + nearestStylusTarget.distance < SCALED_TABLET_MAX_TOUCH_DISTANCE && Vec3.dot(this.stylusTip.velocity, nearestStylusTarget.normal) < 0 && nearestStylusTarget.normalizedPosition.x >= 0 && nearestStylusTarget.normalizedPosition.x <= 1 && nearestStylusTarget.normalizedPosition.y >= 0 && nearestStylusTarget.normalizedPosition.y <= 1) { @@ -647,7 +654,8 @@ Script.include("/~/system/libraries/controllers.js"); }; this.overlayLaserActive = function(controllerData) { - var overlayLaserModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); + var overlayLaserModule = + getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); if (overlayLaserModule) { return overlayLaserModule.isReady(controllerData).active; } diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 9bb3bcb2f9..548179761c 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -8,11 +8,11 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/* jslint bitwise: true */ -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, - getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, - Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, - Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Vec3, + LaserPointers, RayPick, HMD, Uuid, AvatarList */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ @@ -111,7 +111,7 @@ var seatEnd = { url: SEAT_MODEL_URL, dimensions: TARGET_MODEL_DIMENSIONS, ignoreRayIntersection: true -} +}; var teleportRenderStates = [{name: "cancel", path: cancelPath, end: cancelEnd}, {name: "teleport", path: teleportPath, end: teleportEnd}, @@ -228,8 +228,8 @@ function Teleporter(hand) { this.buttonPress = function(value) { _this.buttonValue = value; - } - + }; + this.parameters = makeDispatcherModuleParameters( 80, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], @@ -247,9 +247,36 @@ function Teleporter(hand) { _this.state = TELEPORTER_STATES.TARGETTING; } }, COOL_IN_DURATION); + + // pad scale with avatar size + var AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS = Vec3.multiply(MyAvatar.sensorToWorldScale, TARGET_MODEL_DIMENSIONS); + + if (!Vec3.equal(AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS, cancelEnd.dimensions)) { + cancelEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; + teleportEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; + seatEnd.dimensions = AVATAR_PROPORTIONAL_TARGET_MODEL_DIMENSIONS; + + teleportRenderStates = [{name: "cancel", path: cancelPath, end: cancelEnd}, + {name: "teleport", path: teleportPath, end: teleportEnd}, + {name: "seat", path: seatPath, end: seatEnd}]; + + LaserPointers.editRenderState(this.teleportRayHandVisible, "cancel", teleportRenderStates[0]); + LaserPointers.editRenderState(this.teleportRayHandInvisible, "cancel", teleportRenderStates[0]); + LaserPointers.editRenderState(this.teleportRayHeadVisible, "cancel", teleportRenderStates[0]); + LaserPointers.editRenderState(this.teleportRayHeadInvisible, "cancel", teleportRenderStates[0]); + + LaserPointers.editRenderState(this.teleportRayHandVisible, "teleport", teleportRenderStates[1]); + LaserPointers.editRenderState(this.teleportRayHandInvisible, "teleport", teleportRenderStates[1]); + LaserPointers.editRenderState(this.teleportRayHeadVisible, "teleport", teleportRenderStates[1]); + LaserPointers.editRenderState(this.teleportRayHeadInvisible, "teleport", teleportRenderStates[1]); + + LaserPointers.editRenderState(this.teleportRayHandVisible, "seat", teleportRenderStates[2]); + LaserPointers.editRenderState(this.teleportRayHandInvisible, "seat", teleportRenderStates[2]); + LaserPointers.editRenderState(this.teleportRayHeadVisible, "seat", teleportRenderStates[2]); + LaserPointers.editRenderState(this.teleportRayHeadInvisible, "seat", teleportRenderStates[2]); + } }; - this.isReady = function(controllerData, deltaTime) { var otherModule = this.getOtherModule(); if (_this.buttonValue !== 0 && !otherModule.active) { @@ -259,7 +286,7 @@ function Teleporter(hand) { } return makeRunningValues(false, [], []); }; - + this.run = function(controllerData, deltaTime) { //_this.state = TELEPORTER_STATES.TARGETTING; @@ -301,7 +328,6 @@ function Teleporter(hand) { } else { result = LaserPointers.getPrevRayPickResult(_this.teleportRayHandVisible); } - teleportLocationType = getTeleportTargetType(result); } @@ -327,7 +353,7 @@ function Teleporter(hand) { if (_this.buttonValue !== 0) { return makeRunningValues(true, [], []); } - + if (target === TARGET.NONE || target === TARGET.INVALID || this.state === TELEPORTER_STATES.COOL_IN) { // Do nothing } else if (target === TARGET.SEAT) { @@ -362,7 +388,7 @@ function Teleporter(hand) { } }; } - + // related to repositioning the avatar after you teleport var FOOT_JOINT_NAMES = ["RightToe_End", "RightToeBase", "RightFoot"]; var DEFAULT_ROOT_TO_FOOT_OFFSET = 0.5; @@ -475,7 +501,7 @@ function Teleporter(hand) { disableDispatcherModule("RightTeleporter"); } Script.scriptEnding.connect(cleanup); - + var setIgnoreEntities = function() { LaserPointers.setIgnoreEntities(teleporter.teleportRayRightVisible, ignoredEntities); LaserPointers.setIgnoreEntities(teleporter.teleportRayRightInvisible, ignoredEntities); @@ -483,8 +509,8 @@ function Teleporter(hand) { LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftInvisible, ignoredEntities); LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadVisible, ignoredEntities); LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadInvisible, ignoredEntities); - } - + }; + var isDisabled = false; var handleTeleportMessages = function(channel, message, sender) { if (sender === MyAvatar.sessionUUID) { @@ -501,7 +527,9 @@ function Teleporter(hand) { if (message === 'none') { isDisabled = false; } - } else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) { + } else if (channel === 'Hifi-Teleport-Ignore-Add' && + !Uuid.isNull(message) && + ignoredEntities.indexOf(message) === -1) { ignoredEntities.push(message); setIgnoreEntities(); } else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) { @@ -513,7 +541,7 @@ function Teleporter(hand) { } } }; - + Messages.subscribe('Hifi-Teleport-Disabler'); Messages.subscribe('Hifi-Teleport-Ignore-Add'); Messages.subscribe('Hifi-Teleport-Ignore-Remove'); diff --git a/scripts/system/controllers/controllerModules/webEntityLaserInput.js b/scripts/system/controllers/controllerModules/webEntityLaserInput.js index 1e954d5917..339f248547 100644 --- a/scripts/system/controllers/controllerModules/webEntityLaserInput.js +++ b/scripts/system/controllers/controllerModules/webEntityLaserInput.js @@ -7,13 +7,10 @@ /* jslint bitwise: true */ -/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, - getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, - enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, - makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, +/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Vec3, Quat, getGrabPointSphereOffset, + makeRunningValues, Entities, NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC - + AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, ZERO_VEC, Overlays */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -459,7 +456,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES, maxDistance: PICK_MAX_DISTANCE, - posOffset: getGrabPointSphereOffset(this.handToController()), + posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, defaultRenderStates: defaultRenderStates diff --git a/scripts/system/controllers/touchControllerConfiguration.js b/scripts/system/controllers/touchControllerConfiguration.js index a4622fd5e6..9df8f9e97d 100644 --- a/scripts/system/controllers/touchControllerConfiguration.js +++ b/scripts/system/controllers/touchControllerConfiguration.js @@ -55,6 +55,7 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { type: "static", modelURL: BASE_URL + "Oculus-Labels-L.fbx", naturalPosition: { x: -0.022335469722747803, y: 0.00022516027092933655, z: 0.020340695977211 }, + naturalDimensions: { x: 0.132063, y: 0.0856, z: 0.130282 }, textureName: "blank", defaultTextureLayer: "blank", @@ -84,6 +85,7 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { type: "rotational", modelURL: BASE_URL + "touch_l_trigger.fbx", naturalPosition: { x: 0.0008544912561774254, y: -0.019867943599820137, z: 0.018800459802150726 }, + naturalDimensions: { x: 0.027509, y: 0.025211, z: 0.018443 }, // rotational input: Controller.Standard.LT, @@ -109,6 +111,7 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { type: "linear", modelURL: BASE_URL + "touch_l_bumper.fbx", naturalPosition: { x: 0.00008066371083259583, y: -0.02715788595378399, z: -0.02448512241244316 }, + naturalDimensions: { x: 0.017444, y: 0.020297, z: 0.026003 }, // linear properties // Offset from origin = 0.36470, 0.11048, 0.11066 @@ -132,6 +135,7 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { type: "joystick", modelURL: BASE_URL + "touch_l_joystick.fbx", naturalPosition: { x: 0.0075613949447870255, y: -0.008225866593420506, z: 0.004792703315615654 }, + naturalDimensions: { x: 0.027386, y: 0.033254, z: 0.027272 }, // joystick xInput: "OculusTouch.LX", @@ -156,6 +160,7 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { type: "linear", modelURL: BASE_URL + "touch_l_button_x.fbx", naturalPosition: { x: -0.009307309985160828, y: -0.00005015172064304352, z: -0.012594521045684814 }, + naturalDimensions: { x: 0.009861, y: 0.004345, z: 0.00982 }, input: "OculusTouch.X", axis: { x: 0, y: -1, z: 0 }, @@ -177,6 +182,7 @@ TOUCH_CONTROLLER_CONFIGURATION_LEFT = { type: "linear", modelURL: BASE_URL + "touch_l_button_y.fbx", naturalPosition: { x: -0.01616849936544895, y: -0.000050364527851343155, z: 0.0017703399062156677 }, + naturalDimensions: { x: 0.010014, y: 0.004412, z: 0.009972 }, input: "OculusTouch.Y", axis: { x: 0, y: -1, z: 0 }, @@ -214,6 +220,7 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { type: "static", modelURL: BASE_URL + "Oculus-Labels-R.fbx", naturalPosition: { x: 0.009739525616168976, y: -0.0017818436026573181, z: 0.016794726252555847 }, + naturalDimensions: { x: 0.129049, y: 0.078297, z: 0.139492 }, textureName: "blank", defaultTextureLayer: "blank", @@ -243,6 +250,7 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { type: "rotational", modelURL: BASE_URL + "touch_r_trigger.fbx", naturalPosition: { x: -0.0008544912561774254, y: -0.019867943599820137, z: 0.018800459802150726 }, + naturalDimensions: { x: 0.027384, y: 0.025201, z: 0.018425 }, // rotational input: "OculusTouch.RT", @@ -268,6 +276,7 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { type: "linear", modelURL: BASE_URL + "touch_r_bumper.fbx", naturalPosition: { x: -0.0000806618481874466, y: -0.027157839387655258, z: -0.024485092610120773 }, + naturalDimensions: { x: 0.017268, y: 0.020366, z: 0.02599 }, // linear properties // Offset from origin = 0.36470, 0.11048, 0.11066 @@ -292,6 +301,7 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { type: "joystick", modelURL: BASE_URL + "touch_r_joystick.fbx", naturalPosition: { x: -0.007561382371932268, y: -0.008225853554904461, z: 0.00479268841445446 }, + naturalDimensions: { x: 0.027272, y: 0.033254, z: 0.027272 }, // joystick xInput: "OculusTouch.RX", @@ -316,6 +326,7 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { type: "linear", modelURL: BASE_URL + "touch_r_button_a.fbx", naturalPosition: { x: 0.009307296946644783, y: -0.00005015172064304352, z: -0.012594504281878471 }, + naturalDimensions: { x: 0.00982, y: 0.004345, z: 0.00982 }, input: "OculusTouch.A", axis: { x: 0, y: -1, z: 0 }, @@ -337,6 +348,7 @@ TOUCH_CONTROLLER_CONFIGURATION_RIGHT = { type: "linear", modelURL: BASE_URL + "touch_r_button_b.fbx", naturalPosition: { x: 0.01616847701370716, y: -0.000050364527851343155, z: 0.0017703361809253693 }, + naturalDimensions: { x: 0.009972, y: 0.004412, z: 0.009972 }, input: "OculusTouch.B", axis: { x: 0, y: -1, z: 0 }, diff --git a/scripts/system/controllers/viveControllerConfiguration.js b/scripts/system/controllers/viveControllerConfiguration.js index 51cbb69473..dc4a5b6bb3 100644 --- a/scripts/system/controllers/viveControllerConfiguration.js +++ b/scripts/system/controllers/viveControllerConfiguration.js @@ -62,7 +62,7 @@ var TIP_TEXTURE_BASE_URL = BASE_URL + "meshes/controller/vive_tips.fbm/"; var viveModelURL = BASE_URL + "meshes/controller/vive_body.fbx"; var viveTipsModelURL = BASE_URL + "meshes/controller/vive_tips.fbx"; -var viveTriggerModelURL = "meshes/controller/vive_trigger.fbx" +var viveTriggerModelURL = "meshes/controller/vive_trigger.fbx"; VIVE_CONTROLLER_CONFIGURATION_LEFT = { name: "Vive", @@ -81,6 +81,7 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { type: "static", modelURL: viveTipsModelURL, naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323}, + naturalDimensions: {x: 0.191437, y: 0.094095, z: 0.085656}, textureName: "Tex.Blank", defaultTextureLayer: "blank", @@ -112,6 +113,7 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { xInput: "Vive.LX", yInput: "Vive.LY", naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, + naturalDimensions: {x: 0.042824, y: 0.012537, z: 0.043115}, minValue: 0.0, maxValue: 1.0, minPosition: { x: -0.035, y: 0.004, z: -0.005 }, @@ -137,6 +139,7 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { modelURL: BASE_URL + "meshes/controller/vive_trigger.fbx", input: Controller.Standard.LT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + naturalDimensions: {x: 0.019105, y: 0.022189, z: 0.01909}, origin: { x: 0, y: -0.015, z: -0.00 }, minValue: 0.0, maxValue: 1.0, @@ -147,10 +150,10 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { defaultTextureLayer: "normal", textureLayers: { normal: { - defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/black.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/black.jpg" }, highlight: { - defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/yellow.jpg" } } }, @@ -158,30 +161,35 @@ VIVE_CONTROLLER_CONFIGURATION_LEFT = { l_grip: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_l_grip.fbx", - naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226} + naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + naturalDimensions: {x: 0.010094, y: 0.015064, z: 0.029552} }, r_grip: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_r_grip.fbx", - naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226} + naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + naturalDimensions: {x: 0.010083, y: 0.015064, z: 0.029552} }, sys_button: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_sys_button.fbx", - naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311} + naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, + naturalDimensions: {x: 0.009986, y: 0.004282, z: 0.010264} }, button: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}, + naturalDimensions: {x: 0.009986, y: 0.004496, z: 0.010121} }, button2: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}, + naturalDimensions: {x: 0.009986, y: 0.004496, z: 0.010121} } } } @@ -211,6 +219,7 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { type: "static", modelURL: viveTipsModelURL, naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323}, + naturalDimensions: {x: 0.191437, y: 0.094095, z: 0.085656}, textureName: "Tex.Blank", @@ -243,6 +252,7 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { xInput: "Vive.RX", yInput: "Vive.RY", naturalPosition: { x: 0, y: 0.000979491975158453, z: 0.04872849956154823 }, + naturalDimensions: {x: 0.042824, y: 0.012537, z: 0.043115}, minValue: 0.0, maxValue: 1.0, minPosition: { x: -0.035, y: 0.004, z: -0.005 }, @@ -268,6 +278,7 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { modelURL: BASE_URL + "meshes/controller/vive_trigger.fbx", input: Controller.Standard.RT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + naturalDimensions: {x: 0.019105, y: 0.022189, z: 0.01909}, origin: { x: 0, y: -0.015, z: -0.00 }, minValue: 0.0, maxValue: 1.0, @@ -278,10 +289,10 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { defaultTextureLayer: "normal", textureLayers: { normal: { - defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/black.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/black.jpg" }, highlight: { - defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/yellow.jpg", + defaultTextureURL: BASE_URL + viveTriggerModelURL + "/Trigger.fbm/yellow.jpg" } } }, @@ -289,30 +300,35 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { l_grip: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_l_grip.fbx", - naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226} + naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + naturalDimensions: {x: 0.010094, y: 0.015064, z: 0.029552} }, r_grip: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_r_grip.fbx", - naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226} + naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + naturalDimensions: {x: 0.010083, y: 0.015064, z: 0.029552} }, sys_button: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_sys_button.fbx", - naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311} + naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, + naturalDimensions: {x: 0.009986, y: 0.004282, z: 0.010264} }, button: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}, + naturalDimensions: {x: 0.009986, y: 0.004496, z: 0.010121} }, button2: { type: "static", modelURL: BASE_URL + "meshes/controller/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}, + naturalDimensions: {x: 0.009986, y: 0.004496, z: 0.010121} } } } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 561868f76f..0a5d4026ed 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -655,7 +655,7 @@ var toolBar = (function () { selectionDisplay.triggerMapping.disable(); tablet.landscape = false; } else { - tablet.loadQMLSource("Edit.qml"); + tablet.loadQMLSource("Edit.qml", true); UserActivityLogger.enabledEdit(); entityListTool.setVisible(true); gridTool.setVisible(true); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 3dc62c9e34..d1182f197c 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* global getControllerWorldLocation, Tablet, WebTablet:true, HMD, Settings, Script, - Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp, Controller, Mat4 */ + Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp, Controller, Mat4, resizeTablet */ Script.include(Script.resolvePath("../libraries/utils.js")); Script.include(Script.resolvePath("../libraries/controllers.js")); @@ -42,56 +42,20 @@ var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home- // returns object with two fields: // * position - position in front of the user // * rotation - rotation of entity so it faces the user. -function calcSpawnInfo(hand, tabletHeight, landscape) { +function calcSpawnInfo(hand, landscape) { var finalPosition; var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position; var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation; - if (!hand) { - hand = NO_HANDS; - } - - var handController = null; - if (HMD.active && hand !== NO_HANDS) { - handController = getControllerWorldLocation(hand, true); - } - - if (handController && handController.valid) { - // Orient tablet per hand pitch and yaw. - // Angle it back similar to holding it like a book. - // Move tablet up so that hand is at bottom. - // Move tablet back so that hand is in front. - - var position = handController.position; - var rotation = handController.rotation; - - if (hand === Controller.Standard.LeftHand) { - rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)); - } else { - rotation = Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, -90, 0)); - } - var normal = Vec3.multiplyQbyV(rotation, Vec3.UNIT_NEG_Y); - var lookAt = Quat.lookAt(Vec3.ZERO, normal, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y)); - var TABLET_RAKE_ANGLE = 30; - rotation = Quat.multiply(Quat.angleAxis(TABLET_RAKE_ANGLE, Vec3.multiplyQbyV(lookAt, Vec3.UNIT_X)), lookAt); - - var RELATIVE_SPAWN_OFFSET = { x: 0, y: 0.6, z: 0.1 }; - position = Vec3.sum(position, Vec3.multiplyQbyV(rotation, Vec3.multiply(tabletHeight, RELATIVE_SPAWN_OFFSET))); - - return { - position: position, - rotation: landscape ? Quat.multiply(rotation, { x: 0.0, y: 0.0, z: 0.707, w: 0.707 }) : rotation - }; - } else { - var forward = Quat.getForward(headRot); - finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, forward)); - var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0}); - return { - position: finalPosition, - rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180) - }; - } + var forward = Quat.getForward(headRot); + var FORWARD_OFFSET = 0.6 * MyAvatar.sensorToWorldScale; + finalPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward)); + var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0}); + return { + position: finalPosition, + rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180) + }; } /** @@ -106,19 +70,22 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { var _this = this; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + // scale factor of natural tablet dimensions. - this.width = width || DEFAULT_WIDTH; - var tabletScaleFactor = this.width / TABLET_NATURAL_DIMENSIONS.x; - this.height = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor; - this.depth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor; + var tabletWidth = (width || DEFAULT_WIDTH) * sensorScaleFactor; + var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x; + var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor; + var tabletDepth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor; this.landscape = false; visible = visible === true; + var tabletDpi; if (dpi) { - this.dpi = dpi; + tabletDpi = dpi; } else { - this.dpi = DEFAULT_DPI * (DEFAULT_WIDTH / this.width); + tabletDpi = DEFAULT_DPI * (DEFAULT_WIDTH / tabletWidth); } var modelURL = LOCAL_TABLET_MODEL_PATH; @@ -132,7 +99,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { userData: JSON.stringify({ "grabbableKey": {"grabbable": true} }), - dimensions: this.getDimensions(), + dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }, parentID: AVATAR_SELF_ID, visible: visible }; @@ -152,8 +119,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { Overlays.deleteOverlay(this.webOverlayID); } - var WEB_ENTITY_Z_OFFSET = (this.depth / 2); - var WEB_ENTITY_Y_OFFSET = 0.004; + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2) * (1 / sensorScaleFactor); + var WEB_ENTITY_Y_OFFSET = 0.004 * (1 / sensorScaleFactor); this.webOverlayID = Overlays.addOverlay("web3d", { name: "WebTablet Web", @@ -161,7 +128,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, localRotation: Quat.angleAxis(180, Y_AXIS), resolution: this.getTabletTextureResolution(), - dpi: this.dpi, + dpi: tabletDpi, color: { red: 255, green: 255, blue: 255 }, alpha: 1.0, parentID: this.tabletEntityID, @@ -170,7 +137,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { visible: visible }); - var HOME_BUTTON_Y_OFFSET = (this.height / 2) - (this.height / 20); + var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * (1 / sensorScaleFactor); this.homeButtonID = Overlays.addOverlay("sphere", { name: "homeButton", localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, @@ -247,10 +214,6 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { Camera.modeUpdated.connect(this.myCameraModeChanged); }; -WebTablet.prototype.getDimensions = function() { - return { x: this.width, y: this.height, z: this.depth }; -}; - WebTablet.prototype.getTabletTextureResolution = function() { if (this.landscape) { return { x: TABLET_TEXTURE_RESOLUTION.y , y: TABLET_TEXTURE_RESOLUTION.x }; @@ -301,31 +264,8 @@ WebTablet.prototype.getOverlayObject = function () { }; WebTablet.prototype.setWidth = function (width) { - - // scale factor of natural tablet dimensions. - this.width = width || DEFAULT_WIDTH; - var tabletScaleFactor = this.width / TABLET_NATURAL_DIMENSIONS.x; - this.height = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor; - this.depth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor; - this.dpi = DEFAULT_DPI * (DEFAULT_WIDTH / this.width); - - // update tablet model dimensions - Overlays.editOverlay(this.tabletEntityID, { dimensions: this.getDimensions() }); - - // update webOverlay - var WEB_ENTITY_Z_OFFSET = (this.depth / 2); - var WEB_ENTITY_Y_OFFSET = 0.004; - Overlays.editOverlay(this.webOverlayID, { - localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, - dpi: this.dpi - }); - - // update homeButton - var HOME_BUTTON_Y_OFFSET = (this.height / 2) - (this.height / 20); - Overlays.editOverlay(this.homeButtonID, { - localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, - dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor} - }); + // imported from libraries/utils.js + resizeTablet(width); }; WebTablet.prototype.destroy = function () { @@ -345,11 +285,9 @@ WebTablet.prototype.destroy = function () { WebTablet.prototype.geometryChanged = function (geometry) { if (!HMD.active) { var tabletProperties = {}; - // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - // TODO -- is this still needed? - // Entities.editEntity(this.tabletEntityID, tabletProperties); + Overlays.editOverlay(HMD.tabletID, tabletProperties); } }; @@ -380,10 +318,19 @@ WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos windowPos.y = clamp(windowPos.y, Y_CLAMP, Window.innerHeight - Y_CLAMP); var fov = (Settings.getValue('fieldOfView') || DEFAULT_VERTICAL_FIELD_OF_VIEW) * (Math.PI / 180); + + // scale factor of natural tablet dimensions. + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + var tabletWidth = getTabletWidthFromSettings() * sensorScaleFactor; + var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x; + var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor; + var tabletDepth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor; + var tabletDpi = DEFAULT_DPI * (DEFAULT_WIDTH / tabletWidth); + var MAX_PADDING_FACTOR = 2.2; var PADDING_FACTOR = Math.min(Window.innerHeight / this.getTabletTextureResolution().y, MAX_PADDING_FACTOR); - var TABLET_HEIGHT = (this.getTabletTextureResolution().y / this.dpi) * INCHES_TO_METERS; - var WEB_ENTITY_Z_OFFSET = (this.depth / 2); + var TABLET_HEIGHT = (this.getTabletTextureResolution().y / tabletDpi) * INCHES_TO_METERS; + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2); // calcualte distance from camera var dist = (PADDING_FACTOR * TABLET_HEIGHT) / (2 * Math.tan(fov / 2) * (DESKTOP_TABLET_SCALE / 100)) - WEB_ENTITY_Z_OFFSET; @@ -423,7 +370,7 @@ WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMou tabletProperties.parentJointIndex = SENSOR_TO_ROOM_MATRIX; // compute the appropriate position of the tablet, near the hand controller that was used to spawn it. - var spawnInfo = calcSpawnInfo(hand, this.height, this.landscape); + var spawnInfo = calcSpawnInfo(hand, this.landscape); tabletProperties.position = spawnInfo.position; tabletProperties.rotation = spawnInfo.rotation; } else { @@ -446,12 +393,10 @@ WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMou }; WebTablet.prototype.onHmdChanged = function () { - var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - // TODO -- is this still needed? - // Entities.editEntity(this.tabletEntityID, tabletProperties); + Overlays.editOverlay(HMD.tabletID, tabletProperties); }; WebTablet.prototype.pickle = function () { @@ -531,16 +476,7 @@ WebTablet.prototype.mousePressEvent = function (event) { }; WebTablet.prototype.cameraModeChanged = function (newMode) { - // reposition the tablet. - // This allows HMD.position to reflect the new camera mode. - if (HMD.active) { - var self = this; - var tabletProperties = {}; - // compute position, rotation & parentJointIndex of the tablet - self.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - // TODO -- is this still needed? - // Entities.editEntity(self.tabletEntityID, tabletProperties); - } + ; }; function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index 348155091b..777504b16d 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -5,7 +5,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global entityIsCloneable:true, getGrabbableData:true, cloneEntity:true propsAreCloneDynamic:true */ +/* global entityIsCloneable:true, getGrabbableData:true, cloneEntity:true, propsAreCloneDynamic:true, Script, + propsAreCloneDynamic:true, Entities*/ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 715d520501..33eec74111 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -39,7 +39,8 @@ projectOntoOverlayXYPlane:true, entityHasActions:true, ensureDynamic:true, - findGroupParent:true + findGroupParent:true, + BUMPER_ON_VALUE:true */ MSECS_PER_SEC = 1000.0; diff --git a/scripts/system/libraries/controllers.js b/scripts/system/libraries/controllers.js index 75bca34ed6..d99fd0db48 100644 --- a/scripts/system/libraries/controllers.js +++ b/scripts/system/libraries/controllers.js @@ -18,15 +18,20 @@ getGrabCommunications = function getFarGrabCommunications() { // this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378 var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral -getGrabPointSphereOffset = function(handController) { - if (handController === Controller.Standard.RightHand) { - return GRAB_POINT_SPHERE_OFFSET; +getGrabPointSphereOffset = function(handController, ignoreSensorToWorldScale) { + var offset = GRAB_POINT_SPHERE_OFFSET; + if (handController === Controller.Standard.LeftHand) { + offset = { + x: -GRAB_POINT_SPHERE_OFFSET.x, + y: GRAB_POINT_SPHERE_OFFSET.y, + z: GRAB_POINT_SPHERE_OFFSET.z + }; + } + if (ignoreSensorToWorldScale) { + return offset; + } else { + return Vec3.multiply(MyAvatar.sensorToWorldScale, offset); } - return { - x: GRAB_POINT_SPHERE_OFFSET.x * -1, - y: GRAB_POINT_SPHERE_OFFSET.y, - z: GRAB_POINT_SPHERE_OFFSET.z - }; }; // controllerWorldLocation is where the controller would be, in-world, with an added offset @@ -53,7 +58,7 @@ getControllerWorldLocation = function (handController, doOffset) { } else if (!HMD.isHandControllerAvailable()) { // NOTE: keep this offset in sync with scripts/system/controllers/handControllerPointer.js:493 - var VERTICAL_HEAD_LASER_OFFSET = 0.1; + var VERTICAL_HEAD_LASER_OFFSET = 0.1 * MyAvatar.sensorToWorldScale; position = Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0})); orientation = Quat.multiply(Camera.orientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 })); valid = true; diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index a5e97d8949..162edcaea0 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -351,3 +351,66 @@ clamp = function(val, min, max){ flatten = function(array) { return [].concat.apply([], array); } + +getTabletWidthFromSettings = function () { + var DEFAULT_TABLET_WIDTH = 0.4375; + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var toolbarMode = tablet.toolbarMode; + var DEFAULT_TABLET_SCALE = 100; + var tabletScalePercentage = DEFAULT_TABLET_SCALE; + if (!toolbarMode) { + if (HMD.active) { + tabletScalePercentage = Settings.getValue("hmdTabletScale") || DEFAULT_TABLET_SCALE; + } else { + tabletScalePercentage = Settings.getValue("desktopTabletScale") || DEFAULT_TABLET_SCALE; + } + } + return DEFAULT_TABLET_WIDTH * (tabletScalePercentage / 100); +}; + +resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) { + + if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID) { + return; + } + + var sensorScaleFactor = sensorToWorldScaleOverride || MyAvatar.sensorToWorldScale; + var sensorScaleOffsetOverride = 1; + var SENSOR_TO_ROOM_MATRIX = 65534; + var parentJointIndex = newParentJointIndex || Overlays.getProperty(HMD.tabletID, "parentJointIndex"); + if (parentJointIndex === SENSOR_TO_ROOM_MATRIX) { + sensorScaleOffsetOverride = 1 / sensorScaleFactor; + } + + // will need to be recaclulated if dimensions of fbx model change. + var TABLET_NATURAL_DIMENSIONS = {x: 33.797, y: 50.129, z: 2.269}; + var DEFAULT_DPI = 34; + var DEFAULT_WIDTH = 0.4375; + + // scale factor of natural tablet dimensions. + var tabletWidth = (width || DEFAULT_WIDTH) * sensorScaleFactor; + var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x; + var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor; + var tabletDepth = TABLET_NATURAL_DIMENSIONS.z * tabletScaleFactor; + var tabletDpi = DEFAULT_DPI * (DEFAULT_WIDTH / tabletWidth); + + // update tablet model dimensions + Overlays.editOverlay(HMD.tabletID, { + dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth } + }); + + // update webOverlay + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2) * sensorScaleOffsetOverride; + var WEB_ENTITY_Y_OFFSET = 0.004 * sensorScaleOffsetOverride; + Overlays.editOverlay(HMD.tabletScreenID, { + localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + dpi: tabletDpi + }); + + // update homeButton + var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * sensorScaleOffsetOverride; + Overlays.editOverlay(HMD.homeButtonID, { + localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, + dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor} + }); +}; diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 7b178f53a2..b8e297977a 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -208,7 +208,7 @@ notificationOrientation, notificationPosition, buttonPosition; - + var sensorScaleFactor = MyAvatar.sensorToWorldScale; // Notification plane positions noticeY = -y * NOTIFICATION_3D_SCALE - noticeHeight / 2; notificationPosition = { x: 0, y: noticeY, z: 0 }; @@ -222,8 +222,8 @@ // Translate plane originOffset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, NOTIFICATIONS_3D_DIRECTION, 0), - { x: 0, y: 0, z: -NOTIFICATIONS_3D_DISTANCE }); - originOffset.y += NOTIFICATIONS_3D_ELEVATION; + { x: 0, y: 0, z: -NOTIFICATIONS_3D_DISTANCE * sensorScaleFactor}); + originOffset.y += NOTIFICATIONS_3D_ELEVATION * sensorScaleFactor; notificationPosition = Vec3.sum(originOffset, notificationPosition); buttonPosition = Vec3.sum(originOffset, buttonPosition); @@ -243,14 +243,14 @@ noticeHeight, positions, last; - + var sensorScaleFactor = MyAvatar.sensorToWorldScale; if (isOnHMD) { // Calculate 3D values from 2D overlay properties. noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; noticeHeight = notice.height * NOTIFICATION_3D_SCALE; - notice.size = { x: noticeWidth, y: noticeHeight }; + notice.size = { x: noticeWidth, y: noticeHeight}; positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); @@ -262,7 +262,7 @@ notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE; notice.bottomMargin = 0; notice.rightMargin = 0; - notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE; + notice.lineHeight = 10.0 * (fontSize * sensorScaleFactor / 12.0) * NOTIFICATION_3D_SCALE; notice.isFacingAvatar = false; notificationText = Overlays.addOverlay("text3d", notice); @@ -367,6 +367,7 @@ buttonProperties, i; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; if (text.length >= breakPoint) { breaks = count; } @@ -388,7 +389,7 @@ alpha: backgroundAlpha, topMargin: topMargin, leftMargin: leftMargin, - font: {size: fontSize}, + font: {size: fontSize * sensorScaleFactor}, text: text }; @@ -449,7 +450,23 @@ return finishedLines.join('\n'); } + function updateNotificationsTexts() { + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + for (var i = 0; i < notifications.length; i++) { + var overlayType = Overlays.getOverlayType(notifications[i]); + + if (overlayType === "text3d") { + var props = { + font: {size: fontSize * sensorScaleFactor}, + lineHeight: 10.0 * (fontSize * sensorScaleFactor / 12.0) * NOTIFICATION_3D_SCALE + }; + Overlays.editOverlay(notifications[i], props); + } + } + } + function update() { + updateNotificationsTexts(); var noticeOut, buttonOut, arraysOut, diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 257a56bf09..63c1cc51aa 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -71,9 +71,9 @@ return tabletScalePercentage; } - function updateTabletWidthFromSettings() { + function updateTabletWidthFromSettings(force) { var newTabletScalePercentage = getTabletScalePercentageFromSettings(); - if (newTabletScalePercentage !== tabletScalePercentage && UIWebTablet) { + if ((force || (newTabletScalePercentage !== tabletScalePercentage)) && UIWebTablet) { tabletScalePercentage = newTabletScalePercentage; UIWebTablet.setWidth(DEFAULT_WIDTH * (tabletScalePercentage / 100)); } @@ -83,6 +83,13 @@ updateTabletWidthFromSettings(); } + function onSensorToWorldScaleChanged(sensorScaleFactor) { + if (HMD.active) { + var newTabletScalePercentage = getTabletScalePercentageFromSettings(); + resizeTablet(DEFAULT_WIDTH * (newTabletScalePercentage / 100), undefined, sensorScaleFactor); + } + } + function rezTablet() { if (debugTablet) { print("TABLET rezzing"); @@ -98,6 +105,7 @@ HMD.homeButtonID = UIWebTablet.homeButtonID; HMD.tabletScreenID = UIWebTablet.webOverlayID; HMD.displayModeChanged.connect(onHmdChanged); + MyAvatar.sensorToWorldScaleChanged.connect(onSensorToWorldScaleChanged); tabletRezzed = true; } @@ -121,6 +129,7 @@ Overlays.editOverlay(HMD.homeButtonID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 }); + updateTabletWidthFromSettings(true); } gTablet.tabletShown = true; } @@ -185,7 +194,9 @@ if (now - validCheckTime > MSECS_PER_SEC) { validCheckTime = now; + updateTabletWidthFromSettings(); + if (UIWebTablet) { UIWebTablet.setLandscape(landscape); } diff --git a/tools/dimensions.mel b/tools/dimensions.mel new file mode 100644 index 0000000000..c477fd04e8 --- /dev/null +++ b/tools/dimensions.mel @@ -0,0 +1,30 @@ +// +// dimensions.mel +// +// Created by Anthony J. Thibault on September 5th, 2017. +// Copyright 2017 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 +// +// Maya Mel script to determine the High Fidelity "naturalDimensions" of a model. + +// get a list of all mesh objects +string $meshes[] = `ls -type mesh`; + +// compute the bounding box +float $boundingBox[] = `polyEvaluate -boundingBox $meshes`; + +// copy values into variables for readability +float $xmin = $boundingBox[0]; +float $xmax = $boundingBox[1]; +float $ymin = $boundingBox[2]; +float $ymax = $boundingBox[3]; +float $zmin = $boundingBox[4]; +float $zmax = $boundingBox[5]; + +// compute dimensions, and convert from cm to meters +vector $dim = <<($xmax - $xmin) / 100.0, ($ymax - $ymin) / 100.0, ($zmax - $zmin) / 100.0>>; + +// print result +print $dim;