mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 03:40:20 +02:00
Merge with master
This commit is contained in:
commit
e395a51c60
70 changed files with 1250 additions and 873 deletions
|
@ -191,7 +191,7 @@ endif()
|
||||||
# link required hifi libraries
|
# link required hifi libraries
|
||||||
link_hifi_libraries(
|
link_hifi_libraries(
|
||||||
shared octree ktx gpu gl gpu-gl procedural model render
|
shared octree ktx gpu gl gpu-gl procedural model render
|
||||||
recording fbx networking model-networking entities avatars
|
recording fbx networking model-networking entities avatars trackers
|
||||||
audio audio-client animation script-engine physics
|
audio audio-client animation script-engine physics
|
||||||
render-utils entities-renderer avatars-renderer ui auto-updater
|
render-utils entities-renderer avatars-renderer ui auto-updater
|
||||||
controllers plugins image trackers
|
controllers plugins image trackers
|
||||||
|
|
|
@ -156,11 +156,9 @@ Column {
|
||||||
function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches
|
function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches
|
||||||
var words = filter.toUpperCase().split(/\s+/).filter(identity);
|
var words = filter.toUpperCase().split(/\s+/).filter(identity);
|
||||||
function suggestable(story) {
|
function suggestable(story) {
|
||||||
if (story.action === 'snapshot') {
|
// We could filter out places we don't want to suggest, such as those where (story.place_name === AddressManager.placename) or (story.username === Account.username).
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return story.place_name !== AddressManager.placename; // Not our entry, but do show other entry points to current domain.
|
|
||||||
}
|
|
||||||
function matches(story) {
|
function matches(story) {
|
||||||
if (!words.length) {
|
if (!words.length) {
|
||||||
return suggestable(story);
|
return suggestable(story);
|
||||||
|
|
|
@ -129,12 +129,12 @@
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <display-plugins/CompositorHelper.h>
|
#include <display-plugins/CompositorHelper.h>
|
||||||
#include <trackers/EyeTracker.h>
|
#include <trackers/EyeTracker.h>
|
||||||
|
#include <avatars-renderer/ScriptAvatar.h>
|
||||||
|
|
||||||
#include "AudioClient.h"
|
#include "AudioClient.h"
|
||||||
#include "audio/AudioScope.h"
|
#include "audio/AudioScope.h"
|
||||||
#include "avatar/AvatarManager.h"
|
#include "avatar/AvatarManager.h"
|
||||||
#include "avatar/ScriptAvatar.h"
|
#include "avatar/MyHead.h"
|
||||||
#include "CrashHandler.h"
|
#include "CrashHandler.h"
|
||||||
#include "devices/DdeFaceTracker.h"
|
#include "devices/DdeFaceTracker.h"
|
||||||
#include "devices/Leapmotion.h"
|
#include "devices/Leapmotion.h"
|
||||||
|
@ -1586,6 +1586,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose);
|
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose);
|
||||||
|
|
||||||
updateSystemTabletMode();
|
updateSystemTabletMode();
|
||||||
|
|
||||||
|
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||||
|
@ -2191,7 +2193,7 @@ void Application::paintGL() {
|
||||||
_myCamera.setOrientation(glm::quat_cast(camMat));
|
_myCamera.setOrientation(glm::quat_cast(camMat));
|
||||||
} else {
|
} else {
|
||||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition());
|
_myCamera.setPosition(myAvatar->getDefaultEyePosition());
|
||||||
_myCamera.setOrientation(myAvatar->getHead()->getCameraOrientation());
|
_myCamera.setOrientation(myAvatar->getMyHead()->getCameraOrientation());
|
||||||
}
|
}
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||||
if (isHMDMode()) {
|
if (isHMDMode()) {
|
||||||
|
@ -4087,6 +4089,30 @@ void Application::cycleCamera() {
|
||||||
cameraMenuChanged(); // handle the menu change
|
cameraMenuChanged(); // handle the menu change
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::cameraModeChanged() {
|
||||||
|
switch (_myCamera.getMode()) {
|
||||||
|
case CAMERA_MODE_FIRST_PERSON:
|
||||||
|
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||||
|
break;
|
||||||
|
case CAMERA_MODE_THIRD_PERSON:
|
||||||
|
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||||
|
break;
|
||||||
|
case CAMERA_MODE_MIRROR:
|
||||||
|
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||||
|
break;
|
||||||
|
case CAMERA_MODE_INDEPENDENT:
|
||||||
|
Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true);
|
||||||
|
break;
|
||||||
|
case CAMERA_MODE_ENTITY:
|
||||||
|
Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cameraMenuChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Application::cameraMenuChanged() {
|
void Application::cameraMenuChanged() {
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||||
|
|
|
@ -373,6 +373,7 @@ public slots:
|
||||||
static void showHelp();
|
static void showHelp();
|
||||||
|
|
||||||
void cycleCamera();
|
void cycleCamera();
|
||||||
|
void cameraModeChanged();
|
||||||
void cameraMenuChanged();
|
void cameraMenuChanged();
|
||||||
void toggleOverlays();
|
void toggleOverlays();
|
||||||
void setOverlaysVisible(bool visible);
|
void setOverlaysVisible(bool visible);
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
PickRay FancyCamera::computePickRay(float x, float y) const {
|
||||||
|
return qApp->computePickRay(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
QUuid FancyCamera::getCameraEntity() const {
|
QUuid FancyCamera::getCameraEntity() const {
|
||||||
if (_cameraEntity != nullptr) {
|
if (_cameraEntity != nullptr) {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#ifndef hifi_FancyCamera_h
|
#ifndef hifi_FancyCamera_h
|
||||||
#define hifi_FancyCamera_h
|
#define hifi_FancyCamera_h
|
||||||
|
|
||||||
#include "Camera.h"
|
#include <shared/Camera.h>
|
||||||
|
|
||||||
#include <EntityTypes.h>
|
#include <EntityTypes.h>
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@ public:
|
||||||
FancyCamera() : Camera() {}
|
FancyCamera() : Camera() {}
|
||||||
|
|
||||||
EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; }
|
EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; }
|
||||||
|
PickRay computePickRay(float x, float y) const override;
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QUuid getCameraEntity() const;
|
QUuid getCameraEntity() const;
|
||||||
|
|
|
@ -142,11 +142,6 @@ void renderWorldBox(gpu::Batch& batch) {
|
||||||
geometryCache->renderSolidSphereInstance(batch, GREY);
|
geometryCache->renderSolidSphereInstance(batch, GREY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a random vector of average length 1
|
|
||||||
const glm::vec3 randVector() {
|
|
||||||
return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do some basic timing tests and report the results
|
// Do some basic timing tests and report the results
|
||||||
void runTimingTests() {
|
void runTimingTests() {
|
||||||
// How long does it take to make a call to get the time?
|
// How long does it take to make a call to get the time?
|
||||||
|
|
|
@ -17,9 +17,6 @@
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
|
|
||||||
float randFloat();
|
|
||||||
const glm::vec3 randVector();
|
|
||||||
|
|
||||||
void renderWorldBox(gpu::Batch& batch);
|
void renderWorldBox(gpu::Batch& batch);
|
||||||
|
|
||||||
void runTimingTests();
|
void runTimingTests();
|
||||||
|
|
|
@ -32,9 +32,9 @@
|
||||||
#include <SettingHandle.h>
|
#include <SettingHandle.h>
|
||||||
#include <UsersScriptingInterface.h>
|
#include <UsersScriptingInterface.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
#include <avatars-renderer/OtherAvatar.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Avatar.h"
|
|
||||||
#include "AvatarManager.h"
|
#include "AvatarManager.h"
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
@ -299,7 +299,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||||
return std::make_shared<Avatar>(qApp->thread(), std::make_shared<Rig>());
|
return std::make_shared<OtherAvatar>(qApp->thread(), std::make_shared<Rig>());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
|
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
|
||||||
|
|
|
@ -21,13 +21,11 @@
|
||||||
#include <PIDController.h>
|
#include <PIDController.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
#include <shared/RateCounter.h>
|
#include <shared/RateCounter.h>
|
||||||
|
#include <avatars-renderer/AvatarMotionState.h>
|
||||||
|
#include <avatars-renderer/ScriptAvatar.h>
|
||||||
|
|
||||||
#include "Avatar.h"
|
|
||||||
#include "MyAvatar.h"
|
#include "MyAvatar.h"
|
||||||
#include "AvatarMotionState.h"
|
|
||||||
#include "ScriptAvatar.h"
|
|
||||||
|
|
||||||
class MyAvatar;
|
|
||||||
class AudioInjector;
|
class AudioInjector;
|
||||||
|
|
||||||
class AvatarManager : public AvatarHashMap {
|
class AvatarManager : public AvatarHashMap {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "MyAvatar.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -43,11 +45,12 @@
|
||||||
#include <RecordingScriptingInterface.h>
|
#include <RecordingScriptingInterface.h>
|
||||||
#include <trackers/FaceTracker.h>
|
#include <trackers/FaceTracker.h>
|
||||||
|
|
||||||
|
#include "MyHead.h"
|
||||||
|
#include "MySkeletonModel.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "AvatarManager.h"
|
#include "AvatarManager.h"
|
||||||
#include "AvatarActionHold.h"
|
#include "AvatarActionHold.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "MyAvatar.h"
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
#include "DebugDraw.h"
|
#include "DebugDraw.h"
|
||||||
|
@ -96,23 +99,12 @@ static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.91
|
||||||
|
|
||||||
MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
|
MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
|
||||||
Avatar(thread, rig),
|
Avatar(thread, rig),
|
||||||
_wasPushing(false),
|
|
||||||
_isPushing(false),
|
|
||||||
_isBeingPushed(false),
|
|
||||||
_isBraking(false),
|
|
||||||
_isAway(false),
|
|
||||||
_boomLength(ZOOM_DEFAULT),
|
|
||||||
_yawSpeed(YAW_SPEED_DEFAULT),
|
_yawSpeed(YAW_SPEED_DEFAULT),
|
||||||
_pitchSpeed(PITCH_SPEED_DEFAULT),
|
_pitchSpeed(PITCH_SPEED_DEFAULT),
|
||||||
_thrust(0.0f),
|
|
||||||
_actionMotorVelocity(0.0f),
|
|
||||||
_scriptedMotorVelocity(0.0f),
|
|
||||||
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
|
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
|
||||||
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
|
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
|
||||||
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
||||||
_characterController(this),
|
_characterController(this),
|
||||||
_lookAtTargetAvatar(),
|
|
||||||
_shouldRender(true),
|
|
||||||
_eyeContactTarget(LEFT_EYE),
|
_eyeContactTarget(LEFT_EYE),
|
||||||
_realWorldFieldOfView("realWorldFieldOfView",
|
_realWorldFieldOfView("realWorldFieldOfView",
|
||||||
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||||
|
@ -129,6 +121,14 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
|
||||||
_audioListenerMode(FROM_HEAD),
|
_audioListenerMode(FROM_HEAD),
|
||||||
_hmdAtRestDetector(glm::vec3(0), glm::quat())
|
_hmdAtRestDetector(glm::vec3(0), glm::quat())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||||
|
_headData = new MyHead(this);
|
||||||
|
|
||||||
|
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr, rig);
|
||||||
|
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
||||||
|
|
||||||
|
|
||||||
using namespace recording;
|
using namespace recording;
|
||||||
_skeletonModel->flagAsCauterized();
|
_skeletonModel->flagAsCauterized();
|
||||||
|
|
||||||
|
@ -536,7 +536,7 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
}
|
}
|
||||||
head->setPosition(headPosition);
|
head->setPosition(headPosition);
|
||||||
head->setScale(getUniformScale());
|
head->setScale(getUniformScale());
|
||||||
head->simulate(deltaTime, true);
|
head->simulate(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record avatars movements.
|
// Record avatars movements.
|
||||||
|
@ -1450,12 +1450,12 @@ void MyAvatar::updateMotors() {
|
||||||
glm::quat motorRotation;
|
glm::quat motorRotation;
|
||||||
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
||||||
if (_characterController.getState() == CharacterController::State::Hover) {
|
if (_characterController.getState() == CharacterController::State::Hover) {
|
||||||
motorRotation = getHead()->getCameraOrientation();
|
motorRotation = getMyHead()->getCameraOrientation();
|
||||||
} else {
|
} else {
|
||||||
// non-hovering = walking: follow camera twist about vertical but not lift
|
// non-hovering = walking: follow camera twist about vertical but not lift
|
||||||
// so we decompose camera's rotation and store the twist part in motorRotation
|
// so we decompose camera's rotation and store the twist part in motorRotation
|
||||||
glm::quat liftRotation;
|
glm::quat liftRotation;
|
||||||
swingTwistDecomposition(getHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation);
|
swingTwistDecomposition(getMyHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation);
|
||||||
}
|
}
|
||||||
const float DEFAULT_MOTOR_TIMESCALE = 0.2f;
|
const float DEFAULT_MOTOR_TIMESCALE = 0.2f;
|
||||||
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
|
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
|
||||||
|
@ -1469,7 +1469,7 @@ void MyAvatar::updateMotors() {
|
||||||
}
|
}
|
||||||
if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) {
|
if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) {
|
||||||
if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) {
|
if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) {
|
||||||
motorRotation = getHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);
|
motorRotation = getMyHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);
|
||||||
} else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) {
|
} else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) {
|
||||||
motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);
|
motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1814,7 +1814,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
||||||
if (getCharacterController()->getState() == CharacterController::State::Hover) {
|
if (getCharacterController()->getState() == CharacterController::State::Hover) {
|
||||||
|
|
||||||
// This is the direction the user desires to fly in.
|
// This is the direction the user desires to fly in.
|
||||||
glm::vec3 desiredFacing = getHead()->getCameraOrientation() * Vectors::UNIT_Z;
|
glm::vec3 desiredFacing = getMyHead()->getCameraOrientation() * Vectors::UNIT_Z;
|
||||||
desiredFacing.y = 0.0f;
|
desiredFacing.y = 0.0f;
|
||||||
|
|
||||||
// This is our reference frame, it is captured when the user begins to move.
|
// This is our reference frame, it is captured when the user begins to move.
|
||||||
|
@ -1958,7 +1958,7 @@ void MyAvatar::updatePosition(float deltaTime) {
|
||||||
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) {
|
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) {
|
||||||
_hoverReferenceCameraFacingIsCaptured = true;
|
_hoverReferenceCameraFacingIsCaptured = true;
|
||||||
// transform the camera facing vector into sensor space.
|
// transform the camera facing vector into sensor space.
|
||||||
_hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z);
|
_hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getMyHead()->getCameraOrientation() * Vectors::UNIT_Z);
|
||||||
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) {
|
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) {
|
||||||
_hoverReferenceCameraFacingIsCaptured = false;
|
_hoverReferenceCameraFacingIsCaptured = false;
|
||||||
}
|
}
|
||||||
|
@ -2804,3 +2804,7 @@ void MyAvatar::updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose&
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MyHead* MyAvatar::getMyHead() const {
|
||||||
|
return static_cast<const MyHead*>(getHead());
|
||||||
|
}
|
||||||
|
|
|
@ -22,14 +22,15 @@
|
||||||
|
|
||||||
#include <controllers/Pose.h>
|
#include <controllers/Pose.h>
|
||||||
#include <controllers/Actions.h>
|
#include <controllers/Actions.h>
|
||||||
|
#include <avatars-renderer/Avatar.h>
|
||||||
|
|
||||||
#include "Avatar.h"
|
|
||||||
#include "AtRestDetector.h"
|
#include "AtRestDetector.h"
|
||||||
#include "MyCharacterController.h"
|
#include "MyCharacterController.h"
|
||||||
#include <ThreadSafeValueCache.h>
|
#include <ThreadSafeValueCache.h>
|
||||||
|
|
||||||
class AvatarActionHold;
|
class AvatarActionHold;
|
||||||
class ModelItemID;
|
class ModelItemID;
|
||||||
|
class MyHead;
|
||||||
|
|
||||||
enum eyeContactTarget {
|
enum eyeContactTarget {
|
||||||
LEFT_EYE,
|
LEFT_EYE,
|
||||||
|
@ -149,6 +150,7 @@ public:
|
||||||
explicit MyAvatar(QThread* thread, RigPointer rig);
|
explicit MyAvatar(QThread* thread, RigPointer rig);
|
||||||
~MyAvatar();
|
~MyAvatar();
|
||||||
|
|
||||||
|
void instantiableAvatar() override {};
|
||||||
void registerMetaTypes(QScriptEngine* engine);
|
void registerMetaTypes(QScriptEngine* engine);
|
||||||
|
|
||||||
virtual void simulateAttachments(float deltaTime) override;
|
virtual void simulateAttachments(float deltaTime) override;
|
||||||
|
@ -353,6 +355,7 @@ public:
|
||||||
|
|
||||||
eyeContactTarget getEyeContactTarget();
|
eyeContactTarget getEyeContactTarget();
|
||||||
|
|
||||||
|
const MyHead* getMyHead() const;
|
||||||
Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); }
|
Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); }
|
||||||
Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); }
|
Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); }
|
||||||
Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); }
|
Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); }
|
||||||
|
@ -589,17 +592,17 @@ private:
|
||||||
std::array<float, MAX_DRIVE_KEYS> _driveKeys;
|
std::array<float, MAX_DRIVE_KEYS> _driveKeys;
|
||||||
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
|
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
|
||||||
|
|
||||||
bool _wasPushing;
|
bool _wasPushing { false };
|
||||||
bool _isPushing;
|
bool _isPushing { false };
|
||||||
bool _isBeingPushed;
|
bool _isBeingPushed { false };
|
||||||
bool _isBraking;
|
bool _isBraking { false };
|
||||||
bool _isAway;
|
bool _isAway { false };
|
||||||
|
|
||||||
float _boomLength;
|
float _boomLength { ZOOM_DEFAULT };
|
||||||
float _yawSpeed; // degrees/sec
|
float _yawSpeed; // degrees/sec
|
||||||
float _pitchSpeed; // degrees/sec
|
float _pitchSpeed; // degrees/sec
|
||||||
|
|
||||||
glm::vec3 _thrust; // impulse accumulator for outside sources
|
glm::vec3 _thrust { 0.0f }; // impulse accumulator for outside sources
|
||||||
|
|
||||||
glm::vec3 _actionMotorVelocity; // target local-frame velocity of avatar (default controller actions)
|
glm::vec3 _actionMotorVelocity; // target local-frame velocity of avatar (default controller actions)
|
||||||
glm::vec3 _scriptedMotorVelocity; // target local-frame velocity of avatar (analog script)
|
glm::vec3 _scriptedMotorVelocity; // target local-frame velocity of avatar (analog script)
|
||||||
|
@ -615,7 +618,7 @@ private:
|
||||||
|
|
||||||
AvatarWeakPointer _lookAtTargetAvatar;
|
AvatarWeakPointer _lookAtTargetAvatar;
|
||||||
glm::vec3 _targetAvatarPosition;
|
glm::vec3 _targetAvatarPosition;
|
||||||
bool _shouldRender;
|
bool _shouldRender { true };
|
||||||
float _oculusYawOffset;
|
float _oculusYawOffset;
|
||||||
|
|
||||||
eyeContactTarget _eyeContactTarget;
|
eyeContactTarget _eyeContactTarget;
|
||||||
|
|
76
interface/src/avatar/MyHead.cpp
Normal file
76
interface/src/avatar/MyHead.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/04/27
|
||||||
|
// Copyright 2013-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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MyHead.h"
|
||||||
|
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
#include <gpu/Batch.h>
|
||||||
|
|
||||||
|
#include <NodeList.h>
|
||||||
|
#include <recording/Deck.h>
|
||||||
|
#include <Rig.h>
|
||||||
|
#include <trackers/FaceTracker.h>
|
||||||
|
#include <trackers/EyeTracker.h>
|
||||||
|
|
||||||
|
#include "devices/DdeFaceTracker.h"
|
||||||
|
#include "Application.h"
|
||||||
|
#include "MyAvatar.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) {
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::quat MyHead::getCameraOrientation() const {
|
||||||
|
// NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so
|
||||||
|
// you may wonder why this code is here. This method will be called while in Oculus mode to determine how
|
||||||
|
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
|
||||||
|
// head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
|
||||||
|
// always the same.
|
||||||
|
if (qApp->isHMDMode()) {
|
||||||
|
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||||
|
return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();
|
||||||
|
} else {
|
||||||
|
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
||||||
|
return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyHead::simulate(float deltaTime) {
|
||||||
|
auto player = DependencyManager::get<recording::Deck>();
|
||||||
|
// Only use face trackers when not playing back a recording.
|
||||||
|
if (!player->isPlaying()) {
|
||||||
|
FaceTracker* faceTracker = qApp->getActiveFaceTracker();
|
||||||
|
_isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted();
|
||||||
|
if (_isFaceTrackerConnected) {
|
||||||
|
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
|
||||||
|
|
||||||
|
if (typeid(*faceTracker) == typeid(DdeFaceTracker)) {
|
||||||
|
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) {
|
||||||
|
calculateMouthShapes(deltaTime);
|
||||||
|
|
||||||
|
const int JAW_OPEN_BLENDSHAPE = 21;
|
||||||
|
const int MMMM_BLENDSHAPE = 34;
|
||||||
|
const int FUNNEL_BLENDSHAPE = 40;
|
||||||
|
const int SMILE_LEFT_BLENDSHAPE = 28;
|
||||||
|
const int SMILE_RIGHT_BLENDSHAPE = 29;
|
||||||
|
_blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen;
|
||||||
|
_blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4;
|
||||||
|
_blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4;
|
||||||
|
_blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2;
|
||||||
|
_blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3;
|
||||||
|
}
|
||||||
|
applyEyelidOffset(getFinalOrientationInWorldFrame());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto eyeTracker = DependencyManager::get<EyeTracker>();
|
||||||
|
_isEyeTrackerConnected = eyeTracker->isTracking();
|
||||||
|
}
|
||||||
|
Parent::simulate(deltaTime);
|
||||||
|
}
|
30
interface/src/avatar/MyHead.h
Normal file
30
interface/src/avatar/MyHead.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/04/27
|
||||||
|
// Copyright 2013-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_MyHead_h
|
||||||
|
#define hifi_MyHead_h
|
||||||
|
|
||||||
|
#include <avatars-renderer/Head.h>
|
||||||
|
|
||||||
|
class MyAvatar;
|
||||||
|
class MyHead : public Head {
|
||||||
|
using Parent = Head;
|
||||||
|
public:
|
||||||
|
explicit MyHead(MyAvatar* owningAvatar);
|
||||||
|
|
||||||
|
/// \return orientationBody * orientationBasePitch
|
||||||
|
glm::quat getCameraOrientation() const;
|
||||||
|
void simulate(float deltaTime) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
||||||
|
MyHead(const Head&);
|
||||||
|
MyHead& operator= (const MyHead&);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_MyHead_h
|
158
interface/src/avatar/MySkeletonModel.cpp
Normal file
158
interface/src/avatar/MySkeletonModel.cpp
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/04/27
|
||||||
|
// Copyright 2013-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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MySkeletonModel.h"
|
||||||
|
|
||||||
|
#include <avatars-renderer/Avatar.h>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "InterfaceLogging.h"
|
||||||
|
|
||||||
|
MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : SkeletonModel(owningAvatar, parent, rig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) {
|
||||||
|
switch (state) {
|
||||||
|
default:
|
||||||
|
case CharacterController::State::Ground:
|
||||||
|
return Rig::CharacterControllerState::Ground;
|
||||||
|
case CharacterController::State::Takeoff:
|
||||||
|
return Rig::CharacterControllerState::Takeoff;
|
||||||
|
case CharacterController::State::InAir:
|
||||||
|
return Rig::CharacterControllerState::InAir;
|
||||||
|
case CharacterController::State::Hover:
|
||||||
|
return Rig::CharacterControllerState::Hover;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called within Model::simulate call, below.
|
||||||
|
void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
|
const FBXGeometry& geometry = getFBXGeometry();
|
||||||
|
|
||||||
|
Head* head = _owningAvatar->getHead();
|
||||||
|
|
||||||
|
// make sure lookAt is not too close to face (avoid crosseyes)
|
||||||
|
glm::vec3 lookAt = _owningAvatar->isMyAvatar() ? head->getLookAtPosition() : head->getCorrectedLookAtPosition();
|
||||||
|
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||||
|
|
||||||
|
Rig::HeadParameters headParams;
|
||||||
|
|
||||||
|
// input action is the highest priority source for head orientation.
|
||||||
|
auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame();
|
||||||
|
if (avatarHeadPose.isValid()) {
|
||||||
|
glm::mat4 rigHeadMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());
|
||||||
|
headParams.rigHeadPosition = extractTranslation(rigHeadMat);
|
||||||
|
headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat);
|
||||||
|
headParams.headEnabled = true;
|
||||||
|
} else {
|
||||||
|
if (qApp->isHMDMode()) {
|
||||||
|
// get HMD position from sensor space into world space, and back into rig space
|
||||||
|
glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
||||||
|
glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation());
|
||||||
|
glm::mat4 worldToRig = glm::inverse(rigToWorld);
|
||||||
|
glm::mat4 rigHMDMat = worldToRig * worldHMDMat;
|
||||||
|
_rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation);
|
||||||
|
headParams.headEnabled = true;
|
||||||
|
} else {
|
||||||
|
// even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode.
|
||||||
|
// preMult 180 is necessary to convert from avatar to rig coordinates.
|
||||||
|
// postMult 180 is necessary to convert head from -z forward to z forward.
|
||||||
|
headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
|
||||||
|
headParams.headEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame();
|
||||||
|
if (avatarHipsPose.isValid()) {
|
||||||
|
glm::mat4 rigHipsMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation());
|
||||||
|
headParams.hipsMatrix = rigHipsMat;
|
||||||
|
headParams.hipsEnabled = true;
|
||||||
|
} else {
|
||||||
|
headParams.hipsEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame();
|
||||||
|
if (avatarSpine2Pose.isValid()) {
|
||||||
|
glm::mat4 rigSpine2Mat = Matrices::Y_180 * createMatFromQuatAndPos(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation());
|
||||||
|
headParams.spine2Matrix = rigSpine2Mat;
|
||||||
|
headParams.spine2Enabled = true;
|
||||||
|
} else {
|
||||||
|
headParams.spine2Enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f;
|
||||||
|
|
||||||
|
_rig->updateFromHeadParameters(headParams, deltaTime);
|
||||||
|
|
||||||
|
Rig::HandAndFeetParameters handAndFeetParams;
|
||||||
|
|
||||||
|
auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
|
||||||
|
if (leftPose.isValid()) {
|
||||||
|
handAndFeetParams.isLeftEnabled = true;
|
||||||
|
handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation();
|
||||||
|
handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation();
|
||||||
|
} else {
|
||||||
|
handAndFeetParams.isLeftEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame();
|
||||||
|
if (rightPose.isValid()) {
|
||||||
|
handAndFeetParams.isRightEnabled = true;
|
||||||
|
handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation();
|
||||||
|
handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation();
|
||||||
|
} else {
|
||||||
|
handAndFeetParams.isRightEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame();
|
||||||
|
if (leftFootPose.isValid()) {
|
||||||
|
handAndFeetParams.isLeftFootEnabled = true;
|
||||||
|
handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation();
|
||||||
|
handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation();
|
||||||
|
} else {
|
||||||
|
handAndFeetParams.isLeftFootEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame();
|
||||||
|
if (rightFootPose.isValid()) {
|
||||||
|
handAndFeetParams.isRightFootEnabled = true;
|
||||||
|
handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation();
|
||||||
|
handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation();
|
||||||
|
} else {
|
||||||
|
handAndFeetParams.isRightFootEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
|
||||||
|
handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
|
||||||
|
handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
|
||||||
|
|
||||||
|
_rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime);
|
||||||
|
|
||||||
|
Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState());
|
||||||
|
|
||||||
|
auto velocity = myAvatar->getLocalVelocity();
|
||||||
|
auto position = myAvatar->getLocalPosition();
|
||||||
|
auto orientation = myAvatar->getLocalOrientation();
|
||||||
|
_rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState);
|
||||||
|
|
||||||
|
// evaluate AnimGraph animation and update jointStates.
|
||||||
|
Model::updateRig(deltaTime, parentTransform);
|
||||||
|
|
||||||
|
Rig::EyeParameters eyeParams;
|
||||||
|
eyeParams.eyeLookAt = lookAt;
|
||||||
|
eyeParams.eyeSaccade = head->getSaccade();
|
||||||
|
eyeParams.modelRotation = getRotation();
|
||||||
|
eyeParams.modelTranslation = getTranslation();
|
||||||
|
eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex;
|
||||||
|
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
|
||||||
|
|
||||||
|
_rig->updateFromEyeParameters(eyeParams);
|
||||||
|
|
||||||
|
Parent::updateRig(deltaTime, parentTransform);
|
||||||
|
}
|
||||||
|
|
26
interface/src/avatar/MySkeletonModel.h
Normal file
26
interface/src/avatar/MySkeletonModel.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/04/27
|
||||||
|
// Copyright 2013-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_MySkeletonModel_h
|
||||||
|
#define hifi_MySkeletonModel_h
|
||||||
|
|
||||||
|
#include <avatars-renderer/SkeletonModel.h>
|
||||||
|
|
||||||
|
/// A skeleton loaded from a model.
|
||||||
|
class MySkeletonModel : public SkeletonModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Parent = SkeletonModel;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr);
|
||||||
|
void updateRig(float deltaTime, glm::mat4 parentTransform) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_MySkeletonModel_h
|
|
@ -9,20 +9,21 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "DdeFaceTracker.h"
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QJsonArray>
|
#include <QtCore/QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
#include <QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
#include <FaceshiftConstants.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "DdeFaceTracker.h"
|
|
||||||
#include "FaceshiftConstants.h"
|
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef hifi_DdeFaceTracker_h
|
#ifndef hifi_DdeFaceTracker_h
|
||||||
#define hifi_DdeFaceTracker_h
|
#define hifi_DdeFaceTracker_h
|
||||||
|
|
||||||
|
#include <QtCore/QtGlobal>
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
|
#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
|
||||||
#define HAVE_DDE
|
#define HAVE_DDE
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
//
|
//
|
||||||
// Leapmotion.cpp
|
|
||||||
// interface/src/devices
|
|
||||||
//
|
|
||||||
// Created by Sam Cake on 6/2/2014
|
// Created by Sam Cake on 6/2/2014
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
|
@ -10,10 +7,11 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Leapmotion.h"
|
#include "Leapmotion.h"
|
||||||
#include "Menu.h"
|
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
|
#include "Menu.h"
|
||||||
|
|
||||||
const int PALMROOT_NUM_JOINTS = 3;
|
const int PALMROOT_NUM_JOINTS = 3;
|
||||||
const int FINGER_NUM_JOINTS = 4;
|
const int FINGER_NUM_JOINTS = 4;
|
||||||
const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS;
|
const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS;
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
//
|
//
|
||||||
// Leapmotion.h
|
|
||||||
// interface/src/devices
|
|
||||||
//
|
|
||||||
// Created by Sam Cake on 6/2/2014
|
// Created by Sam Cake on 6/2/2014
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
|
|
|
@ -22,6 +22,7 @@ ModelOverlay::ModelOverlay()
|
||||||
_modelTextures(QVariantMap())
|
_modelTextures(QVariantMap())
|
||||||
{
|
{
|
||||||
_model->init();
|
_model->init();
|
||||||
|
_model->setLoadingPriority(_loadPriority);
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +31,11 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
||||||
_model(std::make_shared<Model>(std::make_shared<Rig>(), nullptr, this)),
|
_model(std::make_shared<Model>(std::make_shared<Rig>(), nullptr, this)),
|
||||||
_modelTextures(QVariantMap()),
|
_modelTextures(QVariantMap()),
|
||||||
_url(modelOverlay->_url),
|
_url(modelOverlay->_url),
|
||||||
_updateModel(false)
|
_updateModel(false),
|
||||||
|
_loadPriority(modelOverlay->getLoadPriority())
|
||||||
{
|
{
|
||||||
_model->init();
|
_model->init();
|
||||||
|
_model->setLoadingPriority(_loadPriority);
|
||||||
if (_url.isValid()) {
|
if (_url.isValid()) {
|
||||||
_updateModel = true;
|
_updateModel = true;
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
|
@ -113,6 +116,12 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
||||||
_updateModel = true;
|
_updateModel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto loadPriorityProperty = properties["loadPriority"];
|
||||||
|
if (loadPriorityProperty.isValid()) {
|
||||||
|
_loadPriority = loadPriorityProperty.toFloat();
|
||||||
|
_model->setLoadingPriority(_loadPriority);
|
||||||
|
}
|
||||||
|
|
||||||
auto urlValue = properties["url"];
|
auto urlValue = properties["url"];
|
||||||
if (urlValue.isValid() && urlValue.canConvert<QString>()) {
|
if (urlValue.isValid() && urlValue.canConvert<QString>()) {
|
||||||
_url = urlValue.toString();
|
_url = urlValue.toString();
|
||||||
|
|
|
@ -41,6 +41,8 @@ public:
|
||||||
|
|
||||||
void locationChanged(bool tellPhysics) override;
|
void locationChanged(bool tellPhysics) override;
|
||||||
|
|
||||||
|
float getLoadPriority() const { return _loadPriority; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// helper to extract metadata from our Model's rigged joints
|
// helper to extract metadata from our Model's rigged joints
|
||||||
template <typename itemType> using mapFunction = std::function<itemType(int jointIndex)>;
|
template <typename itemType> using mapFunction = std::function<itemType(int jointIndex)>;
|
||||||
|
@ -55,6 +57,7 @@ private:
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
bool _updateModel = { false };
|
bool _updateModel = { false };
|
||||||
bool _scaleToFit = { false };
|
bool _scaleToFit = { false };
|
||||||
|
float _loadPriority { 0.0f };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ModelOverlay_h
|
#endif // hifi_ModelOverlay_h
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
set(TARGET_NAME avatars-renderer)
|
set(TARGET_NAME avatars-renderer)
|
||||||
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
|
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
|
||||||
setup_hifi_library(Widgets Network Script)
|
setup_hifi_library(Widgets Network Script)
|
||||||
link_hifi_libraries(shared gpu model animation physics model-networking script-engine render render-utils)
|
link_hifi_libraries(shared gpu model animation physics model-networking script-engine render image render-utils)
|
||||||
|
|
||||||
target_bullet()
|
target_bullet()
|
||||||
|
|
|
@ -27,16 +27,13 @@
|
||||||
#include <TextRenderer3D.h>
|
#include <TextRenderer3D.h>
|
||||||
#include <VariantMapToScriptValue.h>
|
#include <VariantMapToScriptValue.h>
|
||||||
#include <DebugDraw.h>
|
#include <DebugDraw.h>
|
||||||
|
#include <shared/Camera.h>
|
||||||
|
#include <SoftAttachmentModel.h>
|
||||||
|
|
||||||
#include "AvatarMotionState.h"
|
#include "Logging.h"
|
||||||
#include "Camera.h"
|
|
||||||
#include "InterfaceLogging.h"
|
|
||||||
#include "SceneScriptingInterface.h"
|
|
||||||
#include "SoftAttachmentModel.h"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
|
|
||||||
const int NUM_BODY_CONE_SIDES = 9;
|
const int NUM_BODY_CONE_SIDES = 9;
|
||||||
const float CHAT_MESSAGE_SCALE = 0.0015f;
|
const float CHAT_MESSAGE_SCALE = 0.0015f;
|
||||||
const float CHAT_MESSAGE_HEIGHT = 0.1f;
|
const float CHAT_MESSAGE_HEIGHT = 0.1f;
|
||||||
|
@ -71,6 +68,11 @@ namespace render {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool showAvatars { true };
|
||||||
|
void Avatar::setShowAvatars(bool render) {
|
||||||
|
showAvatars = render;
|
||||||
|
}
|
||||||
|
|
||||||
static bool showReceiveStats = false;
|
static bool showReceiveStats = false;
|
||||||
void Avatar::setShowReceiveStats(bool receiveStats) {
|
void Avatar::setShowReceiveStats(bool receiveStats) {
|
||||||
showReceiveStats = receiveStats;
|
showReceiveStats = receiveStats;
|
||||||
|
@ -97,25 +99,6 @@ void Avatar::setShowNamesAboveHeads(bool show) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar::Avatar(QThread* thread, RigPointer rig) :
|
Avatar::Avatar(QThread* thread, RigPointer rig) :
|
||||||
AvatarData(),
|
|
||||||
_skeletonOffset(0.0f),
|
|
||||||
_bodyYawDelta(0.0f),
|
|
||||||
_positionDeltaAccumulator(0.0f),
|
|
||||||
_lastVelocity(0.0f),
|
|
||||||
_acceleration(0.0f),
|
|
||||||
_lastAngularVelocity(0.0f),
|
|
||||||
_lastOrientation(),
|
|
||||||
_worldUpDirection(DEFAULT_UP_DIRECTION),
|
|
||||||
_moving(false),
|
|
||||||
_smoothPositionTime(SMOOTH_TIME_POSITION),
|
|
||||||
_smoothPositionTimer(std::numeric_limits<float>::max()),
|
|
||||||
_smoothOrientationTime(SMOOTH_TIME_ORIENTATION),
|
|
||||||
_smoothOrientationTimer(std::numeric_limits<float>::max()),
|
|
||||||
_smoothPositionInitial(),
|
|
||||||
_smoothPositionTarget(),
|
|
||||||
_smoothOrientationInitial(),
|
|
||||||
_smoothOrientationTarget(),
|
|
||||||
_initialized(false),
|
|
||||||
_voiceSphereID(GeometryCache::UNKNOWN_ID)
|
_voiceSphereID(GeometryCache::UNKNOWN_ID)
|
||||||
{
|
{
|
||||||
// we may have been created in the network thread, but we live in the main thread
|
// we may have been created in the network thread, but we live in the main thread
|
||||||
|
@ -123,12 +106,6 @@ Avatar::Avatar(QThread* thread, RigPointer rig) :
|
||||||
|
|
||||||
setScale(glm::vec3(1.0f)); // avatar scale is uniform
|
setScale(glm::vec3(1.0f)); // avatar scale is uniform
|
||||||
|
|
||||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
|
||||||
_headData = static_cast<HeadData*>(new Head(this));
|
|
||||||
|
|
||||||
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr, rig);
|
|
||||||
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
|
||||||
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
_nameRectGeometryID = geometryCache->allocateID();
|
_nameRectGeometryID = geometryCache->allocateID();
|
||||||
_leftPointerGeometryID = geometryCache->allocateID();
|
_leftPointerGeometryID = geometryCache->allocateID();
|
||||||
|
@ -283,7 +260,7 @@ void Avatar::updateAvatarEntities() {
|
||||||
// and either add or update the entity.
|
// and either add or update the entity.
|
||||||
QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(data);
|
QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(data);
|
||||||
if (!jsonProperties.isObject()) {
|
if (!jsonProperties.isObject()) {
|
||||||
qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(data.toHex());
|
qCDebug(avatars_renderer) << "got bad avatarEntity json" << QString(data.toHex());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +283,7 @@ void Avatar::updateAvatarEntities() {
|
||||||
// NOTE: if this avatar entity is not attached to us, strip its entity script completely...
|
// NOTE: if this avatar entity is not attached to us, strip its entity script completely...
|
||||||
auto attachedScript = properties.getScript();
|
auto attachedScript = properties.getScript();
|
||||||
if (!isMyAvatar() && !attachedScript.isEmpty()) {
|
if (!isMyAvatar() && !attachedScript.isEmpty()) {
|
||||||
qCDebug(interfaceapp) << "removing entity script from avatar attached entity:" << entityID << "old script:" << attachedScript;
|
qCDebug(avatars_renderer) << "removing entity script from avatar attached entity:" << entityID << "old script:" << attachedScript;
|
||||||
QString noScript;
|
QString noScript;
|
||||||
properties.setScript(noScript);
|
properties.setScript(noScript);
|
||||||
}
|
}
|
||||||
|
@ -410,7 +387,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
||||||
Head* head = getHead();
|
Head* head = getHead();
|
||||||
head->setPosition(headPosition);
|
head->setPosition(headPosition);
|
||||||
head->setScale(getUniformScale());
|
head->setScale(getUniformScale());
|
||||||
head->simulate(deltaTime, false);
|
head->simulate(deltaTime);
|
||||||
} else {
|
} else {
|
||||||
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
|
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
|
||||||
_skeletonModel->simulate(deltaTime, false);
|
_skeletonModel->simulate(deltaTime, false);
|
||||||
|
@ -525,14 +502,15 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
|
||||||
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||||
auto avatarPayload = new render::Payload<AvatarData>(self);
|
auto avatarPayload = new render::Payload<AvatarData>(self);
|
||||||
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
||||||
if (_skeletonModel->addToScene(scene, transaction)) {
|
|
||||||
_renderItemID = scene->allocateID();
|
|
||||||
transaction.resetItem(_renderItemID, avatarPayloadPointer);
|
|
||||||
|
|
||||||
|
if (_renderItemID == render::Item::INVALID_ITEM_ID) {
|
||||||
|
_renderItemID = scene->allocateID();
|
||||||
|
}
|
||||||
|
transaction.resetItem(_renderItemID, avatarPayloadPointer);
|
||||||
|
_skeletonModel->addToScene(scene, transaction);
|
||||||
for (auto& attachmentModel : _attachmentModels) {
|
for (auto& attachmentModel : _attachmentModels) {
|
||||||
attachmentModel->addToScene(scene, transaction);
|
attachmentModel->addToScene(scene, transaction);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||||
|
@ -748,12 +726,12 @@ float Avatar::getBoundingRadius() const {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void debugValue(const QString& str, const glm::vec3& value) {
|
void debugValue(const QString& str, const glm::vec3& value) {
|
||||||
if (glm::any(glm::isnan(value)) || glm::any(glm::isinf(value))) {
|
if (glm::any(glm::isnan(value)) || glm::any(glm::isinf(value))) {
|
||||||
qCWarning(interfaceapp) << "debugValue() " << str << value;
|
qCWarning(avatars_renderer) << "debugValue() " << str << value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
void debugValue(const QString& str, const float& value) {
|
void debugValue(const QString& str, const float& value) {
|
||||||
if (glm::isnan(value) || glm::isinf(value)) {
|
if (glm::isnan(value) || glm::isinf(value)) {
|
||||||
qCWarning(interfaceapp) << "debugValue() " << str << value;
|
qCWarning(avatars_renderer) << "debugValue() " << str << value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#define DEBUG_VALUE(str, value) debugValue(str, value)
|
#define DEBUG_VALUE(str, value) debugValue(str, value)
|
||||||
|
@ -783,7 +761,7 @@ glm::vec3 Avatar::getDisplayNamePosition() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (glm::any(glm::isnan(namePosition)) || glm::any(glm::isinf(namePosition))) {
|
if (glm::any(glm::isnan(namePosition)) || glm::any(glm::isinf(namePosition))) {
|
||||||
qCWarning(interfaceapp) << "Invalid display name position" << namePosition
|
qCWarning(avatars_renderer) << "Invalid display name position" << namePosition
|
||||||
<< ", setting is to (0.0f, 0.5f, 0.0f)";
|
<< ", setting is to (0.0f, 0.5f, 0.0f)";
|
||||||
namePosition = glm::vec3(0.0f, 0.5f, 0.0f);
|
namePosition = glm::vec3(0.0f, 0.5f, 0.0f);
|
||||||
}
|
}
|
||||||
|
@ -1115,14 +1093,14 @@ void Avatar::setModelURLFinished(bool success) {
|
||||||
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
|
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
|
||||||
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
|
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
|
||||||
_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) {
|
_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) {
|
||||||
qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL
|
qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL
|
||||||
<< "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts.";
|
<< "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts.";
|
||||||
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
|
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
|
||||||
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
|
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
|
||||||
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
|
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
|
||||||
Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl()));
|
Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl()));
|
||||||
} else {
|
} else {
|
||||||
qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL
|
qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL
|
||||||
<< "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts()
|
<< "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts()
|
||||||
<< "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS;
|
<< "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS;
|
||||||
}
|
}
|
||||||
|
@ -1438,7 +1416,7 @@ void Avatar::setParentID(const QUuid& parentID) {
|
||||||
if (success) {
|
if (success) {
|
||||||
setTransform(beforeChangeTransform, success);
|
setTransform(beforeChangeTransform, success);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCDebug(interfaceapp) << "Avatar::setParentID failed to reset avatar's location.";
|
qCDebug(avatars_renderer) << "Avatar::setParentID failed to reset avatar's location.";
|
||||||
}
|
}
|
||||||
if (initialParentID != parentID) {
|
if (initialParentID != parentID) {
|
||||||
_parentChanged = usecTimestampNow();
|
_parentChanged = usecTimestampNow();
|
||||||
|
@ -1456,7 +1434,7 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) {
|
||||||
if (success) {
|
if (success) {
|
||||||
setTransform(beforeChangeTransform, success);
|
setTransform(beforeChangeTransform, success);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCDebug(interfaceapp) << "Avatar::setParentJointIndex failed to reset avatar's location.";
|
qCDebug(avatars_renderer) << "Avatar::setParentJointIndex failed to reset avatar's location.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1488,7 +1466,7 @@ QList<QVariant> Avatar::getSkeleton() {
|
||||||
void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) {
|
void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) {
|
||||||
if (scene) {
|
if (scene) {
|
||||||
auto nodelist = DependencyManager::get<NodeList>();
|
auto nodelist = DependencyManager::get<NodeList>();
|
||||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()
|
if (showAvatars
|
||||||
&& !nodelist->isIgnoringNode(getSessionUUID())
|
&& !nodelist->isIgnoringNode(getSessionUUID())
|
||||||
&& !nodelist->isRadiusIgnoringNode(getSessionUUID())) {
|
&& !nodelist->isRadiusIgnoringNode(getSessionUUID())) {
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
|
@ -1496,7 +1474,7 @@ void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(interfaceapp) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown";
|
qCWarning(avatars_renderer) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
#include <ShapeInfo.h>
|
#include <ShapeInfo.h>
|
||||||
#include <render/Scene.h>
|
#include <render/Scene.h>
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
|
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
|
@ -68,6 +69,7 @@ class Avatar : public AvatarData {
|
||||||
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
|
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static void setShowAvatars(bool render);
|
||||||
static void setShowReceiveStats(bool receiveStats);
|
static void setShowReceiveStats(bool receiveStats);
|
||||||
static void setShowMyLookAtVectors(bool showMine);
|
static void setShowMyLookAtVectors(bool showMine);
|
||||||
static void setShowOtherLookAtVectors(bool showOthers);
|
static void setShowOtherLookAtVectors(bool showOthers);
|
||||||
|
@ -77,6 +79,8 @@ public:
|
||||||
explicit Avatar(QThread* thread, RigPointer rig = nullptr);
|
explicit Avatar(QThread* thread, RigPointer rig = nullptr);
|
||||||
~Avatar();
|
~Avatar();
|
||||||
|
|
||||||
|
virtual void instantiableAvatar() = 0;
|
||||||
|
|
||||||
typedef render::Payload<AvatarData> Payload;
|
typedef render::Payload<AvatarData> Payload;
|
||||||
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
|
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
|
||||||
|
|
||||||
|
@ -251,7 +255,6 @@ public:
|
||||||
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
|
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
|
||||||
bool isMoving() const { return _moving; }
|
bool isMoving() const { return _moving; }
|
||||||
|
|
||||||
//void setMotionState(AvatarMotionState* motionState);
|
|
||||||
void setPhysicsCallback(AvatarPhysicsCallback cb);
|
void setPhysicsCallback(AvatarPhysicsCallback cb);
|
||||||
void addPhysicsFlags(uint32_t flags);
|
void addPhysicsFlags(uint32_t flags);
|
||||||
bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; }
|
bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; }
|
||||||
|
@ -268,7 +271,6 @@ public slots:
|
||||||
void setModelURLFinished(bool success);
|
void setModelURLFinished(bool success);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
const float SMOOTH_TIME_POSITION = 0.125f;
|
const float SMOOTH_TIME_POSITION = 0.125f;
|
||||||
const float SMOOTH_TIME_ORIENTATION = 0.075f;
|
const float SMOOTH_TIME_ORIENTATION = 0.075f;
|
||||||
|
|
||||||
|
@ -282,7 +284,7 @@ protected:
|
||||||
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||||
std::vector<std::shared_ptr<Model>> _attachmentsToDelete;
|
std::vector<std::shared_ptr<Model>> _attachmentsToDelete;
|
||||||
|
|
||||||
float _bodyYawDelta; // degrees/sec
|
float _bodyYawDelta { 0.0f }; // degrees/sec
|
||||||
|
|
||||||
// These position histories and derivatives are in the world-frame.
|
// These position histories and derivatives are in the world-frame.
|
||||||
// The derivatives are the MEASURED results of all external and internal forces
|
// The derivatives are the MEASURED results of all external and internal forces
|
||||||
|
@ -298,9 +300,8 @@ protected:
|
||||||
glm::vec3 _angularAcceleration;
|
glm::vec3 _angularAcceleration;
|
||||||
glm::quat _lastOrientation;
|
glm::quat _lastOrientation;
|
||||||
|
|
||||||
glm::vec3 _worldUpDirection;
|
glm::vec3 _worldUpDirection { Vectors::UP };
|
||||||
float _stringLength;
|
bool _moving { false }; ///< set when position is changing
|
||||||
bool _moving; ///< set when position is changing
|
|
||||||
|
|
||||||
// protected methods...
|
// protected methods...
|
||||||
bool isLookingAtMe(AvatarSharedPointer avatar) const;
|
bool isLookingAtMe(AvatarSharedPointer avatar) const;
|
||||||
|
@ -336,10 +337,10 @@ protected:
|
||||||
RateCounter<> _jointDataSimulationRate;
|
RateCounter<> _jointDataSimulationRate;
|
||||||
|
|
||||||
// Smoothing data for blending from one position/orientation to another on remote agents.
|
// Smoothing data for blending from one position/orientation to another on remote agents.
|
||||||
float _smoothPositionTime;
|
float _smoothPositionTime { SMOOTH_TIME_POSITION };
|
||||||
float _smoothPositionTimer;
|
float _smoothPositionTimer { std::numeric_limits<float>::max() };
|
||||||
float _smoothOrientationTime;
|
float _smoothOrientationTime { SMOOTH_TIME_ORIENTATION };
|
||||||
float _smoothOrientationTimer;
|
float _smoothOrientationTimer { std::numeric_limits<float>::max() };
|
||||||
glm::vec3 _smoothPositionInitial;
|
glm::vec3 _smoothPositionInitial;
|
||||||
glm::vec3 _smoothPositionTarget;
|
glm::vec3 _smoothPositionTarget;
|
||||||
glm::quat _smoothOrientationInitial;
|
glm::quat _smoothOrientationInitial;
|
||||||
|
@ -360,7 +361,7 @@ private:
|
||||||
int _leftPointerGeometryID { 0 };
|
int _leftPointerGeometryID { 0 };
|
||||||
int _rightPointerGeometryID { 0 };
|
int _rightPointerGeometryID { 0 };
|
||||||
int _nameRectGeometryID { 0 };
|
int _nameRectGeometryID { 0 };
|
||||||
bool _initialized;
|
bool _initialized { false };
|
||||||
bool _isLookAtTarget { false };
|
bool _isLookAtTarget { false };
|
||||||
bool _isAnimatingScale { false };
|
bool _isAnimatingScale { false };
|
||||||
|
|
|
@ -9,13 +9,12 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "AvatarMotionState.h"
|
||||||
|
|
||||||
#include <PhysicsCollisionGroups.h>
|
#include <PhysicsCollisionGroups.h>
|
||||||
#include <PhysicsEngine.h>
|
#include <PhysicsEngine.h>
|
||||||
#include <PhysicsHelpers.h>
|
#include <PhysicsHelpers.h>
|
||||||
|
|
||||||
#include "Avatar.h"
|
|
||||||
#include "AvatarMotionState.h"
|
|
||||||
#include "BulletUtil.h"
|
|
||||||
|
|
||||||
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||||
assert(_avatar);
|
assert(_avatar);
|
|
@ -15,8 +15,9 @@
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
#include <ObjectMotionState.h>
|
#include <ObjectMotionState.h>
|
||||||
|
#include <BulletUtil.h>
|
||||||
|
|
||||||
class Avatar;
|
#include "Avatar.h"
|
||||||
|
|
||||||
class AvatarMotionState : public ObjectMotionState {
|
class AvatarMotionState : public ObjectMotionState {
|
||||||
public:
|
public:
|
|
@ -8,55 +8,28 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "Head.h"
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
|
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <recording/Deck.h>
|
#include <recording/Deck.h>
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include <GeometryUtil.h>
|
||||||
|
#include <trackers/FaceTracker.h>
|
||||||
#include <trackers/EyeTracker.h>
|
#include <trackers/EyeTracker.h>
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "Avatar.h"
|
|
||||||
#include "DependencyManager.h"
|
|
||||||
#include "GeometryUtil.h"
|
|
||||||
#include "Head.h"
|
|
||||||
#include "Menu.h"
|
|
||||||
#include "Util.h"
|
|
||||||
#include "devices/DdeFaceTracker.h"
|
|
||||||
#include <Rig.h>
|
#include <Rig.h>
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
static bool fixGaze { false };
|
||||||
|
static bool disableEyelidAdjustment { false };
|
||||||
|
|
||||||
Head::Head(Avatar* owningAvatar) :
|
Head::Head(Avatar* owningAvatar) :
|
||||||
HeadData((AvatarData*)owningAvatar),
|
HeadData(owningAvatar),
|
||||||
_returnHeadToCenter(false),
|
|
||||||
_position(0.0f, 0.0f, 0.0f),
|
|
||||||
_rotation(0.0f, 0.0f, 0.0f),
|
|
||||||
_leftEyePosition(0.0f, 0.0f, 0.0f),
|
|
||||||
_rightEyePosition(0.0f, 0.0f, 0.0f),
|
|
||||||
_eyePosition(0.0f, 0.0f, 0.0f),
|
|
||||||
_scale(1.0f),
|
|
||||||
_lastLoudness(0.0f),
|
|
||||||
_longTermAverageLoudness(-1.0f),
|
|
||||||
_audioAttack(0.0f),
|
|
||||||
_audioJawOpen(0.0f),
|
|
||||||
_trailingAudioJawOpen(0.0f),
|
|
||||||
_mouth2(0.0f),
|
|
||||||
_mouth3(0.0f),
|
|
||||||
_mouth4(0.0f),
|
|
||||||
_mouthTime(0.0f),
|
|
||||||
_saccade(0.0f, 0.0f, 0.0f),
|
|
||||||
_saccadeTarget(0.0f, 0.0f, 0.0f),
|
|
||||||
_leftEyeBlinkVelocity(0.0f),
|
|
||||||
_rightEyeBlinkVelocity(0.0f),
|
|
||||||
_timeWithoutTalking(0.0f),
|
|
||||||
_deltaPitch(0.0f),
|
|
||||||
_deltaYaw(0.0f),
|
|
||||||
_deltaRoll(0.0f),
|
|
||||||
_isCameraMoving(false),
|
|
||||||
_isLookingAtMe(false),
|
|
||||||
_lookingAtMeStarted(0),
|
|
||||||
_wasLastLookingAtMe(0),
|
|
||||||
_leftEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID()),
|
_leftEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID()),
|
||||||
_rightEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID())
|
_rightEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID())
|
||||||
{
|
{
|
||||||
|
@ -69,7 +42,7 @@ void Head::reset() {
|
||||||
_baseYaw = _basePitch = _baseRoll = 0.0f;
|
_baseYaw = _basePitch = _baseRoll = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::simulate(float deltaTime, bool isMine) {
|
void Head::simulate(float deltaTime) {
|
||||||
const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for
|
const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for
|
||||||
|
|
||||||
// grab the audio loudness from the owning avatar, if we have one
|
// grab the audio loudness from the owning avatar, if we have one
|
||||||
|
@ -90,43 +63,7 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
_longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f));
|
_longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMine) {
|
|
||||||
auto player = DependencyManager::get<recording::Deck>();
|
|
||||||
// Only use face trackers when not playing back a recording.
|
|
||||||
if (!player->isPlaying()) {
|
|
||||||
FaceTracker* faceTracker = qApp->getActiveFaceTracker();
|
|
||||||
_isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted();
|
|
||||||
if (_isFaceTrackerConnected) {
|
|
||||||
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
|
|
||||||
|
|
||||||
if (typeid(*faceTracker) == typeid(DdeFaceTracker)) {
|
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) {
|
|
||||||
calculateMouthShapes(deltaTime);
|
|
||||||
|
|
||||||
const int JAW_OPEN_BLENDSHAPE = 21;
|
|
||||||
const int MMMM_BLENDSHAPE = 34;
|
|
||||||
const int FUNNEL_BLENDSHAPE = 40;
|
|
||||||
const int SMILE_LEFT_BLENDSHAPE = 28;
|
|
||||||
const int SMILE_RIGHT_BLENDSHAPE = 29;
|
|
||||||
_blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen;
|
|
||||||
_blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4;
|
|
||||||
_blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4;
|
|
||||||
_blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2;
|
|
||||||
_blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyEyelidOffset(getFinalOrientationInWorldFrame());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto eyeTracker = DependencyManager::get<EyeTracker>();
|
|
||||||
_isEyeTrackerConnected = eyeTracker->isTracking();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isFaceTrackerConnected) {
|
if (!_isFaceTrackerConnected) {
|
||||||
|
|
||||||
if (!_isEyeTrackerConnected) {
|
if (!_isEyeTrackerConnected) {
|
||||||
// Update eye saccades
|
// Update eye saccades
|
||||||
const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
|
const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
|
||||||
|
@ -222,7 +159,7 @@ void Head::simulate(float deltaTime, bool isMine) {
|
||||||
} else {
|
} else {
|
||||||
_saccade = glm::vec3();
|
_saccade = glm::vec3();
|
||||||
}
|
}
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FixGaze)) { // if debug menu turns off, use no saccade
|
if (fixGaze) { // if debug menu turns off, use no saccade
|
||||||
_saccade = glm::vec3();
|
_saccade = glm::vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +214,7 @@ void Head::calculateMouthShapes(float deltaTime) {
|
||||||
void Head::applyEyelidOffset(glm::quat headOrientation) {
|
void Head::applyEyelidOffset(glm::quat headOrientation) {
|
||||||
// Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches.
|
// Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches.
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisableEyelidAdjustment)) {
|
if (disableEyelidAdjustment) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +287,7 @@ glm::vec3 Head::getCorrectedLookAtPosition() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) {
|
void Head::setCorrectedLookAtPosition(const glm::vec3& correctedLookAtPosition) {
|
||||||
if (!isLookingAtMe()) {
|
if (!isLookingAtMe()) {
|
||||||
_lookingAtMeStarted = usecTimestampNow();
|
_lookingAtMeStarted = usecTimestampNow();
|
||||||
}
|
}
|
||||||
|
@ -366,25 +303,6 @@ bool Head::isLookingAtMe() {
|
||||||
return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED;
|
return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::quat Head::getCameraOrientation() const {
|
|
||||||
// NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so
|
|
||||||
// you may wonder why this code is here. This method will be called while in Oculus mode to determine how
|
|
||||||
// to change the driving direction while in Oculus mode. It is used to support driving toward where you're
|
|
||||||
// head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not
|
|
||||||
// always the same.
|
|
||||||
if (qApp->isHMDMode()) {
|
|
||||||
MyAvatar* myAvatar = dynamic_cast<MyAvatar*>(_owningAvatar);
|
|
||||||
if (myAvatar) {
|
|
||||||
return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();
|
|
||||||
} else {
|
|
||||||
return getOrientation();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
|
||||||
return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
|
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
|
||||||
glm::quat orientation = getOrientation();
|
glm::quat orientation = getOrientation();
|
||||||
glm::vec3 lookAtDelta = _lookAtPosition - eyePosition;
|
glm::vec3 lookAtDelta = _lookAtPosition - eyePosition;
|
|
@ -11,16 +11,10 @@
|
||||||
#ifndef hifi_Head_h
|
#ifndef hifi_Head_h
|
||||||
#define hifi_Head_h
|
#define hifi_Head_h
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <GLMHelpers.h>
|
||||||
#include <glm/gtx/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include <HeadData.h>
|
#include <HeadData.h>
|
||||||
|
|
||||||
#include "world.h"
|
|
||||||
|
|
||||||
|
|
||||||
const float EYE_EAR_GAP = 0.08f;
|
const float EYE_EAR_GAP = 0.08f;
|
||||||
|
|
||||||
class Avatar;
|
class Avatar;
|
||||||
|
@ -31,9 +25,9 @@ public:
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void reset();
|
void reset();
|
||||||
void simulate(float deltaTime, bool isMine);
|
virtual void simulate(float deltaTime);
|
||||||
void setScale(float scale);
|
void setScale(float scale);
|
||||||
void setPosition(glm::vec3 position) { _position = position; }
|
void setPosition(const glm::vec3& position) { _position = position; }
|
||||||
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
|
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
|
||||||
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
|
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
|
||||||
|
|
||||||
|
@ -43,17 +37,14 @@ public:
|
||||||
/// \return orientationBody * (orientationBase+Delta)
|
/// \return orientationBody * (orientationBase+Delta)
|
||||||
glm::quat getFinalOrientationInWorldFrame() const;
|
glm::quat getFinalOrientationInWorldFrame() const;
|
||||||
|
|
||||||
/// \return orientationBody * orientationBasePitch
|
void setCorrectedLookAtPosition(const glm::vec3& correctedLookAtPosition);
|
||||||
glm::quat getCameraOrientation () const;
|
|
||||||
|
|
||||||
void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition);
|
|
||||||
glm::vec3 getCorrectedLookAtPosition();
|
glm::vec3 getCorrectedLookAtPosition();
|
||||||
void clearCorrectedLookAtPosition() { _isLookingAtMe = false; }
|
void clearCorrectedLookAtPosition() { _isLookingAtMe = false; }
|
||||||
bool isLookingAtMe();
|
bool isLookingAtMe();
|
||||||
quint64 getLookingAtMeStarted() { return _lookingAtMeStarted; }
|
quint64 getLookingAtMeStarted() { return _lookingAtMeStarted; }
|
||||||
|
|
||||||
float getScale() const { return _scale; }
|
float getScale() const { return _scale; }
|
||||||
glm::vec3 getPosition() const { return _position; }
|
const glm::vec3& getPosition() const { return _position; }
|
||||||
const glm::vec3& getEyePosition() const { return _eyePosition; }
|
const glm::vec3& getEyePosition() const { return _eyePosition; }
|
||||||
const glm::vec3& getSaccade() const { return _saccade; }
|
const glm::vec3& getSaccade() const { return _saccade; }
|
||||||
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||||
|
@ -91,46 +82,46 @@ public:
|
||||||
|
|
||||||
float getTimeWithoutTalking() const { return _timeWithoutTalking; }
|
float getTimeWithoutTalking() const { return _timeWithoutTalking; }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * 0.5f; }
|
glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * 0.5f; }
|
||||||
|
|
||||||
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
||||||
Head(const Head&);
|
Head(const Head&);
|
||||||
Head& operator= (const Head&);
|
Head& operator= (const Head&);
|
||||||
|
|
||||||
bool _returnHeadToCenter;
|
bool _returnHeadToCenter { false };
|
||||||
glm::vec3 _position;
|
glm::vec3 _position;
|
||||||
glm::vec3 _rotation;
|
glm::vec3 _rotation;
|
||||||
glm::vec3 _leftEyePosition;
|
glm::vec3 _leftEyePosition;
|
||||||
glm::vec3 _rightEyePosition;
|
glm::vec3 _rightEyePosition;
|
||||||
glm::vec3 _eyePosition;
|
glm::vec3 _eyePosition;
|
||||||
|
|
||||||
float _scale;
|
float _scale { 1.0f };
|
||||||
float _lastLoudness;
|
float _lastLoudness { 0.0f };
|
||||||
float _longTermAverageLoudness;
|
float _longTermAverageLoudness { -1.0f };
|
||||||
float _audioAttack;
|
float _audioAttack { 0.0f };
|
||||||
float _audioJawOpen;
|
float _audioJawOpen { 0.0f };
|
||||||
float _trailingAudioJawOpen;
|
float _trailingAudioJawOpen { 0.0f };
|
||||||
float _mouth2;
|
float _mouth2 { 0.0f };
|
||||||
float _mouth3;
|
float _mouth3 { 0.0f };
|
||||||
float _mouth4;
|
float _mouth4 { 0.0f };
|
||||||
float _mouthTime;
|
float _mouthTime { 0.0f };
|
||||||
|
|
||||||
glm::vec3 _saccade;
|
glm::vec3 _saccade;
|
||||||
glm::vec3 _saccadeTarget;
|
glm::vec3 _saccadeTarget;
|
||||||
float _leftEyeBlinkVelocity;
|
float _leftEyeBlinkVelocity { 0.0f };
|
||||||
float _rightEyeBlinkVelocity;
|
float _rightEyeBlinkVelocity { 0.0f };
|
||||||
float _timeWithoutTalking;
|
float _timeWithoutTalking { 0.0f };
|
||||||
|
|
||||||
// delta angles for local head rotation (driven by hardware input)
|
// delta angles for local head rotation (driven by hardware input)
|
||||||
float _deltaPitch;
|
float _deltaPitch { 0.0f };
|
||||||
float _deltaYaw;
|
float _deltaYaw { 0.0f };
|
||||||
float _deltaRoll;
|
float _deltaRoll { 0.0f };
|
||||||
|
|
||||||
bool _isCameraMoving;
|
bool _isCameraMoving { false };
|
||||||
bool _isLookingAtMe;
|
bool _isLookingAtMe { false };
|
||||||
quint64 _lookingAtMeStarted;
|
quint64 _lookingAtMeStarted { 0 };
|
||||||
quint64 _wasLastLookingAtMe;
|
quint64 _wasLastLookingAtMe { 0 };
|
||||||
|
|
||||||
glm::vec3 _correctedLookAtPosition;
|
glm::vec3 _correctedLookAtPosition;
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "AvatarsRendererLogging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(avatars_renderer, "hifi.avatars.rendering")
|
Q_LOGGING_CATEGORY(avatars_renderer, "hifi.avatars.rendering")
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/04/27
|
||||||
|
// Copyright 2013-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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "OtherAvatar.h"
|
||||||
|
|
||||||
|
OtherAvatar::OtherAvatar(QThread* thread, RigPointer rig) : Avatar(thread, rig) {
|
||||||
|
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||||
|
_headData = new Head(this);
|
||||||
|
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr, rig);
|
||||||
|
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017/04/27
|
||||||
|
// Copyright 2013-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_OtherAvatar_h
|
||||||
|
#define hifi_OtherAvatar_h
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
|
||||||
|
class OtherAvatar : public Avatar {
|
||||||
|
public:
|
||||||
|
explicit OtherAvatar(QThread* thread, RigPointer rig = nullptr);
|
||||||
|
void instantiableAvatar() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_OtherAvatar_h
|
|
@ -9,19 +9,18 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "SkeletonModel.h"
|
||||||
|
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
|
|
||||||
#include <recording/Deck.h>
|
#include <recording/Deck.h>
|
||||||
#include <DebugDraw.h>
|
#include <DebugDraw.h>
|
||||||
|
#include <AnimDebugDraw.h>
|
||||||
|
#include <CharacterController.h>
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "Menu.h"
|
#include "Logging.h"
|
||||||
#include "SkeletonModel.h"
|
|
||||||
#include "Util.h"
|
|
||||||
#include "InterfaceLogging.h"
|
|
||||||
#include "AnimDebugDraw.h"
|
|
||||||
|
|
||||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
|
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
|
||||||
CauterizedModel(rig, parent),
|
CauterizedModel(rig, parent),
|
||||||
|
@ -47,7 +46,7 @@ void SkeletonModel::initJointStates() {
|
||||||
// Determine the default eye position for avatar scale = 1.0
|
// Determine the default eye position for avatar scale = 1.0
|
||||||
int headJointIndex = geometry.headJointIndex;
|
int headJointIndex = geometry.headJointIndex;
|
||||||
if (0 > headJointIndex || headJointIndex >= _rig->getJointStateCount()) {
|
if (0 > headJointIndex || headJointIndex >= _rig->getJointStateCount()) {
|
||||||
qCWarning(interfaceapp) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount();
|
qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount();
|
||||||
}
|
}
|
||||||
glm::vec3 leftEyePosition, rightEyePosition;
|
glm::vec3 leftEyePosition, rightEyePosition;
|
||||||
getEyeModelPositions(leftEyePosition, rightEyePosition);
|
getEyeModelPositions(leftEyePosition, rightEyePosition);
|
||||||
|
@ -72,21 +71,6 @@ void SkeletonModel::initJointStates() {
|
||||||
emit skeletonLoaded();
|
emit skeletonLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) {
|
|
||||||
switch (state) {
|
|
||||||
default:
|
|
||||||
case CharacterController::State::Ground:
|
|
||||||
return Rig::CharacterControllerState::Ground;
|
|
||||||
case CharacterController::State::Takeoff:
|
|
||||||
return Rig::CharacterControllerState::Takeoff;
|
|
||||||
case CharacterController::State::InAir:
|
|
||||||
return Rig::CharacterControllerState::InAir;
|
|
||||||
case CharacterController::State::Hover:
|
|
||||||
return Rig::CharacterControllerState::Hover;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Called within Model::simulate call, below.
|
// Called within Model::simulate call, below.
|
||||||
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
const FBXGeometry& geometry = getFBXGeometry();
|
const FBXGeometry& geometry = getFBXGeometry();
|
||||||
|
@ -102,122 +86,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
lookAt = _owningAvatar->getHead()->getEyePosition() + (MIN_LOOK_AT_FOCUS_DISTANCE / focusDistance) * focusOffset;
|
lookAt = _owningAvatar->getHead()->getEyePosition() + (MIN_LOOK_AT_FOCUS_DISTANCE / focusDistance) * focusOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_owningAvatar->isMyAvatar()) {
|
if (!_owningAvatar->isMyAvatar()) {
|
||||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
|
||||||
|
|
||||||
Rig::HeadParameters headParams;
|
|
||||||
|
|
||||||
// input action is the highest priority source for head orientation.
|
|
||||||
auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame();
|
|
||||||
if (avatarHeadPose.isValid()) {
|
|
||||||
glm::mat4 rigHeadMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());
|
|
||||||
headParams.rigHeadPosition = extractTranslation(rigHeadMat);
|
|
||||||
headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat);
|
|
||||||
headParams.headEnabled = true;
|
|
||||||
} else {
|
|
||||||
if (qApp->isHMDMode()) {
|
|
||||||
// get HMD position from sensor space into world space, and back into rig space
|
|
||||||
glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
|
||||||
glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation());
|
|
||||||
glm::mat4 worldToRig = glm::inverse(rigToWorld);
|
|
||||||
glm::mat4 rigHMDMat = worldToRig * worldHMDMat;
|
|
||||||
_rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation);
|
|
||||||
headParams.headEnabled = true;
|
|
||||||
} else {
|
|
||||||
// even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode.
|
|
||||||
// preMult 180 is necessary to convert from avatar to rig coordinates.
|
|
||||||
// postMult 180 is necessary to convert head from -z forward to z forward.
|
|
||||||
headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
|
|
||||||
headParams.headEnabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame();
|
|
||||||
if (avatarHipsPose.isValid()) {
|
|
||||||
glm::mat4 rigHipsMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation());
|
|
||||||
headParams.hipsMatrix = rigHipsMat;
|
|
||||||
headParams.hipsEnabled = true;
|
|
||||||
} else {
|
|
||||||
headParams.hipsEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame();
|
|
||||||
if (avatarSpine2Pose.isValid()) {
|
|
||||||
glm::mat4 rigSpine2Mat = Matrices::Y_180 * createMatFromQuatAndPos(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation());
|
|
||||||
headParams.spine2Matrix = rigSpine2Mat;
|
|
||||||
headParams.spine2Enabled = true;
|
|
||||||
} else {
|
|
||||||
headParams.spine2Enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f;
|
|
||||||
|
|
||||||
_rig->updateFromHeadParameters(headParams, deltaTime);
|
|
||||||
|
|
||||||
Rig::HandAndFeetParameters handAndFeetParams;
|
|
||||||
|
|
||||||
auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
|
|
||||||
if (leftPose.isValid()) {
|
|
||||||
handAndFeetParams.isLeftEnabled = true;
|
|
||||||
handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation();
|
|
||||||
handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation();
|
|
||||||
} else {
|
|
||||||
handAndFeetParams.isLeftEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame();
|
|
||||||
if (rightPose.isValid()) {
|
|
||||||
handAndFeetParams.isRightEnabled = true;
|
|
||||||
handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation();
|
|
||||||
handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation();
|
|
||||||
} else {
|
|
||||||
handAndFeetParams.isRightEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame();
|
|
||||||
if (leftFootPose.isValid()) {
|
|
||||||
handAndFeetParams.isLeftFootEnabled = true;
|
|
||||||
handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation();
|
|
||||||
handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation();
|
|
||||||
} else {
|
|
||||||
handAndFeetParams.isLeftFootEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame();
|
|
||||||
if (rightFootPose.isValid()) {
|
|
||||||
handAndFeetParams.isRightFootEnabled = true;
|
|
||||||
handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation();
|
|
||||||
handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation();
|
|
||||||
} else {
|
|
||||||
handAndFeetParams.isRightFootEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
|
|
||||||
handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
|
|
||||||
handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
|
|
||||||
|
|
||||||
_rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime);
|
|
||||||
|
|
||||||
Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState());
|
|
||||||
|
|
||||||
auto velocity = myAvatar->getLocalVelocity();
|
|
||||||
auto position = myAvatar->getLocalPosition();
|
|
||||||
auto orientation = myAvatar->getLocalOrientation();
|
|
||||||
_rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState);
|
|
||||||
|
|
||||||
// evaluate AnimGraph animation and update jointStates.
|
|
||||||
Model::updateRig(deltaTime, parentTransform);
|
|
||||||
|
|
||||||
Rig::EyeParameters eyeParams;
|
|
||||||
eyeParams.eyeLookAt = lookAt;
|
|
||||||
eyeParams.eyeSaccade = head->getSaccade();
|
|
||||||
eyeParams.modelRotation = getRotation();
|
|
||||||
eyeParams.modelTranslation = getTranslation();
|
|
||||||
eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex;
|
|
||||||
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
|
|
||||||
|
|
||||||
_rig->updateFromEyeParameters(eyeParams);
|
|
||||||
} else {
|
|
||||||
// no need to call Model::updateRig() because otherAvatars get their joint state
|
// no need to call Model::updateRig() because otherAvatars get their joint state
|
||||||
// copied directly from AvtarData::_jointData (there are no Rig animations to blend)
|
// copied directly from AvtarData::_jointData (there are no Rig animations to blend)
|
||||||
_needsUpdateClusterMatrices = true;
|
_needsUpdateClusterMatrices = true;
|
||||||
|
@ -249,6 +118,9 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
|
|
||||||
_rig->updateFromEyeParameters(eyeParams);
|
_rig->updateFromEyeParameters(eyeParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// evaluate AnimGraph animation and update jointStates.
|
||||||
|
Model::updateRig(deltaTime, parentTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::updateAttitude() {
|
void SkeletonModel::updateAttitude() {
|
|
@ -114,7 +114,7 @@ protected:
|
||||||
|
|
||||||
void computeBoundingShape();
|
void computeBoundingShape();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||||
|
|
|
@ -94,7 +94,7 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
|
||||||
// +-----+-----+-+-+-+--+
|
// +-----+-----+-+-+-+--+
|
||||||
// Key state - K0,K1 is found in the 1st and 2nd bits
|
// Key state - K0,K1 is found in the 1st and 2nd bits
|
||||||
// Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits
|
// Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits
|
||||||
// Faceshift - F is found in the 5th bit
|
// Face tracker - F is found in the 5th bit
|
||||||
// Eye tracker - E is found in the 6th bit
|
// Eye tracker - E is found in the 6th bit
|
||||||
// Referential Data - R is found in the 7th bit
|
// Referential Data - R is found in the 7th bit
|
||||||
const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits
|
const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits
|
||||||
|
@ -123,7 +123,7 @@ namespace AvatarDataPacket {
|
||||||
// it might be nice to use a dictionary to compress that
|
// it might be nice to use a dictionary to compress that
|
||||||
|
|
||||||
// Packet State Flags - we store the details about the existence of other records in this bitset:
|
// Packet State Flags - we store the details about the existence of other records in this bitset:
|
||||||
// AvatarGlobalPosition, Avatar Faceshift, eye tracking, and existence of
|
// AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of
|
||||||
using HasFlags = uint16_t;
|
using HasFlags = uint16_t;
|
||||||
const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0;
|
const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0;
|
||||||
const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1;
|
const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1;
|
||||||
|
|
|
@ -23,11 +23,6 @@
|
||||||
|
|
||||||
#include "AvatarData.h"
|
#include "AvatarData.h"
|
||||||
|
|
||||||
/// The names of the blendshapes expected by Faceshift, terminated with an empty string.
|
|
||||||
extern const char* FACESHIFT_BLENDSHAPES[];
|
|
||||||
/// The size of FACESHIFT_BLENDSHAPES
|
|
||||||
extern const int NUM_FACESHIFT_BLENDSHAPES;
|
|
||||||
|
|
||||||
HeadData::HeadData(AvatarData* owningAvatar) :
|
HeadData::HeadData(AvatarData* owningAvatar) :
|
||||||
_baseYaw(0.0f),
|
_baseYaw(0.0f),
|
||||||
_basePitch(0.0f),
|
_basePitch(0.0f),
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include <shared/NsightHelpers.h>
|
#include <shared/NsightHelpers.h>
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
|
#include <ResourceManager.h>
|
||||||
|
|
||||||
#include "FBXReader.h"
|
#include "FBXReader.h"
|
||||||
#include "ModelFormatLogging.h"
|
#include "ModelFormatLogging.h"
|
||||||
|
@ -165,6 +166,7 @@ bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex,
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<OBJFace> OBJFace::triangulate() {
|
QVector<OBJFace> OBJFace::triangulate() {
|
||||||
QVector<OBJFace> newFaces;
|
QVector<OBJFace> newFaces;
|
||||||
const int nVerticesInATriangle = 3;
|
const int nVerticesInATriangle = 3;
|
||||||
|
@ -183,6 +185,7 @@ QVector<OBJFace> OBJFace::triangulate() {
|
||||||
}
|
}
|
||||||
return newFaces;
|
return newFaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f at index i
|
void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f at index i
|
||||||
vertexIndices.append(face->vertexIndices[index]);
|
vertexIndices.append(face->vertexIndices[index]);
|
||||||
if (face->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent.
|
if (face->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent.
|
||||||
|
@ -193,24 +196,13 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool replyOK(QNetworkReply* netReply, QUrl url) { // This will be reworked when we make things asynchronous
|
|
||||||
return (netReply && netReply->isFinished() &&
|
|
||||||
(url.toString().startsWith("file", Qt::CaseInsensitive) ? // file urls don't have http status codes
|
|
||||||
netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString().isEmpty() :
|
|
||||||
(netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OBJReader::isValidTexture(const QByteArray &filename) {
|
bool OBJReader::isValidTexture(const QByteArray &filename) {
|
||||||
if (_url.isEmpty()) {
|
if (_url.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QUrl candidateUrl = _url.resolved(QUrl(filename));
|
QUrl candidateUrl = _url.resolved(QUrl(filename));
|
||||||
QNetworkReply *netReply = request(candidateUrl, true);
|
|
||||||
bool isValid = replyOK(netReply, candidateUrl);
|
return ResourceManager::resourceExists(candidateUrl);
|
||||||
if (netReply) {
|
|
||||||
netReply->deleteLater();
|
|
||||||
}
|
|
||||||
return isValid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
||||||
|
@ -274,7 +266,28 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
|
std::tuple<bool, QByteArray> requestData(QUrl& url) {
|
||||||
|
auto request = ResourceManager::createResourceRequest(nullptr, url);
|
||||||
|
|
||||||
|
if (!request) {
|
||||||
|
return std::make_tuple(false, QByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
request->send();
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(request, &ResourceRequest::finished, &loop, &QEventLoop::quit);
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
if (request->getResult() == ResourceRequest::Success) {
|
||||||
|
return std::make_tuple(true, request->getData());
|
||||||
|
} else {
|
||||||
|
return std::make_tuple(false, QByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QNetworkReply* request(QUrl& url, bool isTest) {
|
||||||
if (!qApp) {
|
if (!qApp) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -293,10 +306,7 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
|
||||||
QEventLoop loop; // Create an event loop that will quit when we get the finished signal
|
QEventLoop loop; // Create an event loop that will quit when we get the finished signal
|
||||||
QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit()));
|
QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||||
loop.exec(); // Nothing is going to happen on this whole run thread until we get this
|
loop.exec(); // Nothing is going to happen on this whole run thread until we get this
|
||||||
static const int WAIT_TIMEOUT_MS = 500;
|
|
||||||
while (!aboutToQuit && qApp && !netReply->isReadable()) {
|
|
||||||
netReply->waitForReadyRead(WAIT_TIMEOUT_MS); // so we might as well block this thread waiting for the response, rather than
|
|
||||||
}
|
|
||||||
QObject::disconnect(connection);
|
QObject::disconnect(connection);
|
||||||
return netReply; // trying to sync later on.
|
return netReply; // trying to sync later on.
|
||||||
}
|
}
|
||||||
|
@ -624,15 +634,15 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
||||||
// Throw away any path part of libraryName, and merge against original url.
|
// Throw away any path part of libraryName, and merge against original url.
|
||||||
QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName());
|
QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName());
|
||||||
qCDebug(modelformat) << "OBJ Reader material library" << libraryName << "used in" << _url;
|
qCDebug(modelformat) << "OBJ Reader material library" << libraryName << "used in" << _url;
|
||||||
QNetworkReply* netReply = request(libraryUrl, false);
|
bool success;
|
||||||
if (replyOK(netReply, libraryUrl)) {
|
QByteArray data;
|
||||||
parseMaterialLibrary(netReply);
|
std::tie<bool, QByteArray>(success, data) = requestData(libraryUrl);
|
||||||
|
if (success) {
|
||||||
|
QBuffer buffer { &data };
|
||||||
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
parseMaterialLibrary(&buffer);
|
||||||
} else {
|
} else {
|
||||||
qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer. Got"
|
qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer";
|
||||||
<< (!netReply ? "aborted" : netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
|
|
||||||
}
|
|
||||||
if (netReply) {
|
|
||||||
netReply->deleteLater();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,6 @@ public:
|
||||||
QString currentMaterialName;
|
QString currentMaterialName;
|
||||||
QHash<QString, OBJMaterial> materials;
|
QHash<QString, OBJMaterial> materials;
|
||||||
|
|
||||||
QNetworkReply* request(QUrl& url, bool isTest);
|
|
||||||
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl());
|
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl());
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -525,11 +525,14 @@ void GL41VariableAllocationTexture::populateTransferQueue() {
|
||||||
|
|
||||||
// break down the transfers into chunks so that no single transfer is
|
// break down the transfers into chunks so that no single transfer is
|
||||||
// consuming more than X bandwidth
|
// consuming more than X bandwidth
|
||||||
|
// For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block
|
||||||
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
|
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
|
||||||
const auto lines = mipDimensions.y;
|
const auto lines = mipDimensions.y;
|
||||||
auto bytesPerLine = mipSize / lines;
|
const uint32_t BLOCK_NUM_LINES { 4 };
|
||||||
|
const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES;
|
||||||
|
auto bytesPerBlock = mipSize / numBlocks;
|
||||||
Q_ASSERT(0 == (mipSize % lines));
|
Q_ASSERT(0 == (mipSize % lines));
|
||||||
uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine);
|
uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock);
|
||||||
uint32_t lineOffset = 0;
|
uint32_t lineOffset = 0;
|
||||||
while (lineOffset < lines) {
|
while (lineOffset < lines) {
|
||||||
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
|
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
|
||||||
|
|
|
@ -196,11 +196,14 @@ void GL45ResourceTexture::populateTransferQueue() {
|
||||||
|
|
||||||
// break down the transfers into chunks so that no single transfer is
|
// break down the transfers into chunks so that no single transfer is
|
||||||
// consuming more than X bandwidth
|
// consuming more than X bandwidth
|
||||||
|
// For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block
|
||||||
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
|
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
|
||||||
const auto lines = mipDimensions.y;
|
const auto lines = mipDimensions.y;
|
||||||
auto bytesPerLine = mipSize / lines;
|
const uint32_t BLOCK_NUM_LINES { 4 };
|
||||||
|
const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES;
|
||||||
|
auto bytesPerBlock = mipSize / numBlocks;
|
||||||
Q_ASSERT(0 == (mipSize % lines));
|
Q_ASSERT(0 == (mipSize % lines));
|
||||||
uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine);
|
uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock);
|
||||||
uint32_t lineOffset = 0;
|
uint32_t lineOffset = 0;
|
||||||
while (lineOffset < lines) {
|
while (lineOffset < lines) {
|
||||||
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
|
uint32_t linesToCopy = std::min<uint32_t>(lines - lineOffset, linesPerTransfer);
|
||||||
|
|
|
@ -99,6 +99,61 @@ struct GPUKTXPayload {
|
||||||
};
|
};
|
||||||
const std::string GPUKTXPayload::KEY { "hifi.gpu" };
|
const std::string GPUKTXPayload::KEY { "hifi.gpu" };
|
||||||
|
|
||||||
|
|
||||||
|
struct IrradianceKTXPayload {
|
||||||
|
using Version = uint8;
|
||||||
|
|
||||||
|
static const std::string KEY;
|
||||||
|
static const Version CURRENT_VERSION{ 0 };
|
||||||
|
static const size_t PADDING{ 3 };
|
||||||
|
static const size_t SIZE{ sizeof(Version) + sizeof(SphericalHarmonics) + PADDING };
|
||||||
|
static_assert(IrradianceKTXPayload::SIZE == 148, "Packing size may differ between platforms");
|
||||||
|
static_assert(IrradianceKTXPayload::SIZE % 4 == 0, "IrradianceKTXPayload is not 4 bytes aligned");
|
||||||
|
|
||||||
|
SphericalHarmonics _irradianceSH;
|
||||||
|
|
||||||
|
Byte* serialize(Byte* data) const {
|
||||||
|
*(Version*)data = CURRENT_VERSION;
|
||||||
|
data += sizeof(Version);
|
||||||
|
|
||||||
|
memcpy(data, &_irradianceSH, sizeof(SphericalHarmonics));
|
||||||
|
data += sizeof(SphericalHarmonics);
|
||||||
|
|
||||||
|
return data + PADDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unserialize(const Byte* data, size_t size) {
|
||||||
|
if (size != SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Version version = *(const Version*)data;
|
||||||
|
if (version != CURRENT_VERSION) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data += sizeof(Version);
|
||||||
|
|
||||||
|
memcpy(&_irradianceSH, data, sizeof(SphericalHarmonics));
|
||||||
|
data += sizeof(SphericalHarmonics);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isIrradianceKTX(const ktx::KeyValue& val) {
|
||||||
|
return (val._key.compare(KEY) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool findInKeyValues(const ktx::KeyValues& keyValues, IrradianceKTXPayload& payload) {
|
||||||
|
auto found = std::find_if(keyValues.begin(), keyValues.end(), isIrradianceKTX);
|
||||||
|
if (found != keyValues.end()) {
|
||||||
|
auto value = found->_value;
|
||||||
|
return payload.unserialize(value.data(), value.size());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const std::string IrradianceKTXPayload::KEY{ "hifi.irradianceSH" };
|
||||||
|
|
||||||
KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) {
|
KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) {
|
||||||
{
|
{
|
||||||
// We are doing a lot of work here just to get descriptor data
|
// We are doing a lot of work here just to get descriptor data
|
||||||
|
@ -304,16 +359,27 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GPUKTXPayload keyval;
|
GPUKTXPayload gpuKeyval;
|
||||||
keyval._samplerDesc = texture.getSampler().getDesc();
|
gpuKeyval._samplerDesc = texture.getSampler().getDesc();
|
||||||
keyval._usage = texture.getUsage();
|
gpuKeyval._usage = texture.getUsage();
|
||||||
keyval._usageType = texture.getUsageType();
|
gpuKeyval._usageType = texture.getUsageType();
|
||||||
|
|
||||||
Byte keyvalPayload[GPUKTXPayload::SIZE];
|
Byte keyvalPayload[GPUKTXPayload::SIZE];
|
||||||
keyval.serialize(keyvalPayload);
|
gpuKeyval.serialize(keyvalPayload);
|
||||||
|
|
||||||
ktx::KeyValues keyValues;
|
ktx::KeyValues keyValues;
|
||||||
keyValues.emplace_back(GPUKTXPayload::KEY, (uint32)GPUKTXPayload::SIZE, (ktx::Byte*) &keyvalPayload);
|
keyValues.emplace_back(GPUKTXPayload::KEY, (uint32)GPUKTXPayload::SIZE, (ktx::Byte*) &keyvalPayload);
|
||||||
|
|
||||||
|
if (texture.getIrradiance()) {
|
||||||
|
IrradianceKTXPayload irradianceKeyval;
|
||||||
|
irradianceKeyval._irradianceSH = *texture.getIrradiance();
|
||||||
|
|
||||||
|
Byte irradianceKeyvalPayload[IrradianceKTXPayload::SIZE];
|
||||||
|
irradianceKeyval.serialize(irradianceKeyvalPayload);
|
||||||
|
|
||||||
|
keyValues.emplace_back(IrradianceKTXPayload::KEY, (uint32)IrradianceKTXPayload::SIZE, (ktx::Byte*) &irradianceKeyvalPayload);
|
||||||
|
}
|
||||||
|
|
||||||
auto hash = texture.sourceHash();
|
auto hash = texture.sourceHash();
|
||||||
if (!hash.empty()) {
|
if (!hash.empty()) {
|
||||||
// the sourceHash is an std::string in hex
|
// the sourceHash is an std::string in hex
|
||||||
|
@ -409,6 +475,12 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe
|
||||||
// Assing the mips availables
|
// Assing the mips availables
|
||||||
texture->setStoredMipFormat(mipFormat);
|
texture->setStoredMipFormat(mipFormat);
|
||||||
texture->setKtxBacking(ktxfile);
|
texture->setKtxBacking(ktxfile);
|
||||||
|
|
||||||
|
IrradianceKTXPayload irradianceKtxKeyValue;
|
||||||
|
if (IrradianceKTXPayload::findInKeyValues(descriptor.keyValues, irradianceKtxKeyValue)) {
|
||||||
|
texture->overrideIrradiance(std::make_shared<SphericalHarmonics>(irradianceKtxKeyValue._irradianceSH));
|
||||||
|
}
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,16 +40,16 @@ AssetResourceRequest::~AssetResourceRequest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetResourceRequest::urlIsAssetHash() const {
|
bool AssetResourceRequest::urlIsAssetHash(const QUrl& url) {
|
||||||
static const QString ATP_HASH_REGEX_STRING { "^atp:([A-Fa-f0-9]{64})(\\.[\\w]+)?$" };
|
static const QString ATP_HASH_REGEX_STRING { "^atp:([A-Fa-f0-9]{64})(\\.[\\w]+)?$" };
|
||||||
|
|
||||||
QRegExp hashRegex { ATP_HASH_REGEX_STRING };
|
QRegExp hashRegex { ATP_HASH_REGEX_STRING };
|
||||||
return hashRegex.exactMatch(_url.toString());
|
return hashRegex.exactMatch(url.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetResourceRequest::doSend() {
|
void AssetResourceRequest::doSend() {
|
||||||
// We'll either have a hash or an ATP path to a file (that maps to a hash)
|
// We'll either have a hash or an ATP path to a file (that maps to a hash)
|
||||||
if (urlIsAssetHash()) {
|
if (urlIsAssetHash(_url)) {
|
||||||
// We've detected that this is a hash - simply use AssetClient to request that asset
|
// We've detected that this is a hash - simply use AssetClient to request that asset
|
||||||
auto parts = _url.path().split(".", QString::SkipEmptyParts);
|
auto parts = _url.path().split(".", QString::SkipEmptyParts);
|
||||||
auto hash = parts.length() > 0 ? parts[0] : "";
|
auto hash = parts.length() > 0 ? parts[0] : "";
|
||||||
|
|
|
@ -32,7 +32,7 @@ private slots:
|
||||||
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool urlIsAssetHash() const;
|
static bool urlIsAssetHash(const QUrl& url);
|
||||||
|
|
||||||
void requestMappingForPath(const AssetPath& path);
|
void requestMappingForPath(const AssetPath& path);
|
||||||
void requestHash(const AssetHash& hash);
|
void requestHash(const AssetHash& hash);
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
#include <QNetworkDiskCache>
|
#include <QNetworkDiskCache>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
|
|
||||||
#include "AssetResourceRequest.h"
|
#include "AssetResourceRequest.h"
|
||||||
#include "FileResourceRequest.h"
|
#include "FileResourceRequest.h"
|
||||||
#include "HTTPResourceRequest.h"
|
#include "HTTPResourceRequest.h"
|
||||||
|
@ -116,3 +116,51 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q
|
||||||
request->moveToThread(&_thread);
|
request->moveToThread(&_thread);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ResourceManager::resourceExists(const QUrl& url) {
|
||||||
|
auto scheme = url.scheme();
|
||||||
|
if (scheme == URL_SCHEME_FILE) {
|
||||||
|
QFileInfo file { url.toString() };
|
||||||
|
return file.exists();
|
||||||
|
} else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) {
|
||||||
|
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
QNetworkRequest request { url };
|
||||||
|
|
||||||
|
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||||
|
|
||||||
|
auto reply = networkAccessManager.head(request);
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200;
|
||||||
|
} else if (scheme == URL_SCHEME_ATP) {
|
||||||
|
auto request = new AssetResourceRequest(url);
|
||||||
|
ByteRange range;
|
||||||
|
range.fromInclusive = 1;
|
||||||
|
range.toExclusive = 1;
|
||||||
|
request->setByteRange(range);
|
||||||
|
request->setCacheEnabled(false);
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
|
||||||
|
QObject::connect(request, &AssetResourceRequest::finished, &loop, &QEventLoop::quit);
|
||||||
|
|
||||||
|
request->send();
|
||||||
|
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
request->deleteLater();
|
||||||
|
|
||||||
|
return request->getResult() == ResourceRequest::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ public:
|
||||||
static void init();
|
static void init();
|
||||||
static void cleanup();
|
static void cleanup();
|
||||||
|
|
||||||
|
// Blocking call to check if a resource exists. This function uses a QEventLoop internally
|
||||||
|
// to return to the calling thread so that events can still be processed.
|
||||||
|
static bool resourceExists(const QUrl& url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QThread _thread;
|
static QThread _thread;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
|
||||||
#include "SkeletonModel.h"
|
#include "CauterizedModel.h"
|
||||||
|
|
||||||
using namespace render;
|
using namespace render;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(
|
||||||
|
|
||||||
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
|
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
|
||||||
// Still relying on the raw data from the model
|
// Still relying on the raw data from the model
|
||||||
SkeletonModel* skeleton = static_cast<SkeletonModel*>(_model);
|
CauterizedModel* skeleton = static_cast<CauterizedModel*>(_model);
|
||||||
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization();
|
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization();
|
||||||
|
|
||||||
if (useCauterizedMesh) {
|
if (useCauterizedMesh) {
|
|
@ -1,9 +1,6 @@
|
||||||
//
|
//
|
||||||
// CauterizedModelMeshPartPayload.h
|
|
||||||
// interface/src/avatar
|
|
||||||
//
|
|
||||||
// Created by AndrewMeadows 2017.01.17
|
// Created by AndrewMeadows 2017.01.17
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
// Copyright 2013-2017 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
@ -12,7 +9,7 @@
|
||||||
#ifndef hifi_CauterizedMeshPartPayload_h
|
#ifndef hifi_CauterizedMeshPartPayload_h
|
||||||
#define hifi_CauterizedMeshPartPayload_h
|
#define hifi_CauterizedMeshPartPayload_h
|
||||||
|
|
||||||
#include <MeshPartPayload.h>
|
#include "MeshPartPayload.h"
|
||||||
|
|
||||||
class CauterizedMeshPartPayload : public ModelMeshPartPayload {
|
class CauterizedMeshPartPayload : public ModelMeshPartPayload {
|
||||||
public:
|
public:
|
|
@ -1,7 +1,4 @@
|
||||||
//
|
//
|
||||||
// CauterizedModel.cpp
|
|
||||||
// interface/src/avatar
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows 2017.01.17
|
// Created by Andrew Meadows 2017.01.17
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
|
@ -11,10 +8,10 @@
|
||||||
|
|
||||||
#include "CauterizedModel.h"
|
#include "CauterizedModel.h"
|
||||||
|
|
||||||
#include <AbstractViewStateInterface.h>
|
|
||||||
#include <MeshPartPayload.h>
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
|
||||||
|
#include "AbstractViewStateInterface.h"
|
||||||
|
#include "MeshPartPayload.h"
|
||||||
#include "CauterizedMeshPartPayload.h"
|
#include "CauterizedMeshPartPayload.h"
|
||||||
#include "RenderUtilsLogging.h"
|
#include "RenderUtilsLogging.h"
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
//
|
//
|
||||||
// CauterizeableModel.h
|
|
||||||
// interface/src/avatar
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows 2016.01.17
|
// Created by Andrew Meadows 2016.01.17
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
|
@ -13,7 +10,7 @@
|
||||||
#define hifi_CauterizedModel_h
|
#define hifi_CauterizedModel_h
|
||||||
|
|
||||||
|
|
||||||
#include <Model.h>
|
#include "Model.h"
|
||||||
|
|
||||||
class CauterizedModel : public Model {
|
class CauterizedModel : public Model {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
|
@ -1,7 +1,4 @@
|
||||||
//
|
//
|
||||||
// SoftAttachmentModel.cpp
|
|
||||||
// interface/src/avatar
|
|
||||||
//
|
|
||||||
// Created by Anthony J. Thibault on 12/17/15.
|
// Created by Anthony J. Thibault on 12/17/15.
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
|
@ -10,7 +7,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "SoftAttachmentModel.h"
|
#include "SoftAttachmentModel.h"
|
||||||
#include "InterfaceLogging.h"
|
|
||||||
|
|
||||||
SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) :
|
SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) :
|
||||||
CauterizedModel(rig, parent),
|
CauterizedModel(rig, parent),
|
|
@ -1,7 +1,4 @@
|
||||||
//
|
//
|
||||||
// SoftAttachmentModel.h
|
|
||||||
// interface/src/avatar
|
|
||||||
//
|
|
||||||
// Created by Anthony J. Thibault on 12/17/15.
|
// Created by Anthony J. Thibault on 12/17/15.
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
//
|
//
|
|
@ -582,3 +582,9 @@ glm::mat4 orthoInverse(const glm::mat4& m) {
|
||||||
r[3][3] = 1.0f;
|
r[3][3] = 1.0f;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return a random vector of average length 1
|
||||||
|
glm::vec3 randVector() {
|
||||||
|
return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -252,6 +252,9 @@ inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value
|
||||||
|
|
||||||
glm::mat4 orthoInverse(const glm::mat4& m);
|
glm::mat4 orthoInverse(const glm::mat4& m);
|
||||||
|
|
||||||
|
// Return a random vector of average length 1
|
||||||
|
glm::vec3 randVector();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Safe replacement of glm_mat4_mul() for unaligned arguments instead of __m128
|
// Safe replacement of glm_mat4_mul() for unaligned arguments instead of __m128
|
||||||
//
|
//
|
||||||
|
|
|
@ -8,16 +8,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
#include <EventTypes.h>
|
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "Camera.h"
|
#include "Camera.h"
|
||||||
#include "Menu.h"
|
|
||||||
#include "Util.h"
|
|
||||||
|
|
||||||
|
|
||||||
CameraMode stringToMode(const QString& mode) {
|
CameraMode stringToMode(const QString& mode) {
|
||||||
if (mode == "third person") {
|
if (mode == "third person") {
|
||||||
|
@ -102,35 +93,9 @@ void Camera::setProjection(const glm::mat4& projection) {
|
||||||
_projection = projection;
|
_projection = projection;
|
||||||
}
|
}
|
||||||
|
|
||||||
PickRay Camera::computePickRay(float x, float y) {
|
|
||||||
return qApp->computePickRay(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::setModeString(const QString& mode) {
|
void Camera::setModeString(const QString& mode) {
|
||||||
CameraMode targetMode = stringToMode(mode);
|
CameraMode targetMode = stringToMode(mode);
|
||||||
|
|
||||||
switch (targetMode) {
|
|
||||||
case CAMERA_MODE_FIRST_PERSON:
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
|
||||||
break;
|
|
||||||
case CAMERA_MODE_THIRD_PERSON:
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
|
||||||
break;
|
|
||||||
case CAMERA_MODE_MIRROR:
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
|
||||||
break;
|
|
||||||
case CAMERA_MODE_INDEPENDENT:
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true);
|
|
||||||
break;
|
|
||||||
case CAMERA_MODE_ENTITY:
|
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
qApp->cameraMenuChanged();
|
|
||||||
|
|
||||||
if (_mode != targetMode) {
|
if (_mode != targetMode) {
|
||||||
setMode(targetMode);
|
setMode(targetMode);
|
||||||
}
|
}
|
|
@ -11,11 +11,9 @@
|
||||||
#ifndef hifi_Camera_h
|
#ifndef hifi_Camera_h
|
||||||
#define hifi_Camera_h
|
#define hifi_Camera_h
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include "../GLMHelpers.h"
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include "../RegisteredMetaTypes.h"
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include "../ViewFrustum.h"
|
||||||
#include <RegisteredMetaTypes.h>
|
|
||||||
#include <ViewFrustum.h>
|
|
||||||
|
|
||||||
enum CameraMode
|
enum CameraMode
|
||||||
{
|
{
|
||||||
|
@ -87,7 +85,7 @@ public slots:
|
||||||
* @param {float} y Y-coordinate on screen.
|
* @param {float} y Y-coordinate on screen.
|
||||||
* @return {PickRay} The computed {PickRay}.
|
* @return {PickRay} The computed {PickRay}.
|
||||||
*/
|
*/
|
||||||
PickRay computePickRay(float x, float y);
|
virtual PickRay computePickRay(float x, float y) const = 0;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Set the camera to look at position <code>position</code>. Only works while in <code>independent</code>.
|
* Set the camera to look at position <code>position</code>. Only works while in <code>independent</code>.
|
|
@ -37,15 +37,17 @@ public:
|
||||||
qmlObject->setObjectName(uuid.toString());
|
qmlObject->setObjectName(uuid.toString());
|
||||||
// Make sure we can find it again in the future
|
// Make sure we can find it again in the future
|
||||||
updateQmlItemFromAction();
|
updateQmlItemFromAction();
|
||||||
auto connection = QObject::connect(action, &QAction::changed, [=] {
|
_changedConnection = QObject::connect(action, &QAction::changed, [=] {
|
||||||
updateQmlItemFromAction();
|
updateQmlItemFromAction();
|
||||||
});
|
});
|
||||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] {
|
_shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] {
|
||||||
QObject::disconnect(connection);
|
QObject::disconnect(_changedConnection);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
~MenuUserData() {
|
~MenuUserData() {
|
||||||
|
QObject::disconnect(_changedConnection);
|
||||||
|
QObject::disconnect(_shutdownConnection);
|
||||||
_action->setUserData(USER_DATA_ID, nullptr);
|
_action->setUserData(USER_DATA_ID, nullptr);
|
||||||
_qml->setUserData(USER_DATA_ID, nullptr);
|
_qml->setUserData(USER_DATA_ID, nullptr);
|
||||||
}
|
}
|
||||||
|
@ -104,6 +106,8 @@ public:
|
||||||
private:
|
private:
|
||||||
MenuUserData(const MenuUserData&);
|
MenuUserData(const MenuUserData&);
|
||||||
|
|
||||||
|
QMetaObject::Connection _shutdownConnection;
|
||||||
|
QMetaObject::Connection _changedConnection;
|
||||||
QAction* _action { nullptr };
|
QAction* _action { nullptr };
|
||||||
QObject* _qml { nullptr };
|
QObject* _qml { nullptr };
|
||||||
};
|
};
|
||||||
|
|
|
@ -447,6 +447,10 @@ Grabber.prototype.moveEvent = function(event) {
|
||||||
|
|
||||||
// see if something added/restored gravity
|
// see if something added/restored gravity
|
||||||
var entityProperties = Entities.getEntityProperties(this.entityID);
|
var entityProperties = Entities.getEntityProperties(this.entityID);
|
||||||
|
if (!entityProperties || !entityProperties.gravity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Vec3.length(entityProperties.gravity) !== 0.0) {
|
if (Vec3.length(entityProperties.gravity) !== 0.0) {
|
||||||
this.originalGravity = entityProperties.gravity;
|
this.originalGravity = entityProperties.gravity;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ var WANT_DEBUG = false;
|
||||||
var WANT_DEBUG_STATE = false;
|
var WANT_DEBUG_STATE = false;
|
||||||
var WANT_DEBUG_SEARCH_NAME = null;
|
var WANT_DEBUG_SEARCH_NAME = null;
|
||||||
|
|
||||||
|
var UPDATE_SLEEP_MS = 16; // how many milliseconds to wait between "update" calls
|
||||||
|
|
||||||
var FORCE_IGNORE_IK = false;
|
var FORCE_IGNORE_IK = false;
|
||||||
var SHOW_GRAB_POINT_SPHERE = false;
|
var SHOW_GRAB_POINT_SPHERE = false;
|
||||||
|
|
||||||
|
@ -1333,6 +1335,7 @@ function MyController(hand) {
|
||||||
var stylusProperties = {
|
var stylusProperties = {
|
||||||
name: "stylus",
|
name: "stylus",
|
||||||
url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx",
|
url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx",
|
||||||
|
loadPriority: 10.0,
|
||||||
localPosition: Vec3.sum({ x: 0.0,
|
localPosition: Vec3.sum({ x: 0.0,
|
||||||
y: WEB_TOUCH_Y_OFFSET,
|
y: WEB_TOUCH_Y_OFFSET,
|
||||||
z: 0.0 },
|
z: 0.0 },
|
||||||
|
@ -4091,7 +4094,7 @@ var updateTotalWork = 0;
|
||||||
|
|
||||||
var UPDATE_PERFORMANCE_DEBUGGING = false;
|
var UPDATE_PERFORMANCE_DEBUGGING = false;
|
||||||
|
|
||||||
function updateWrapper(){
|
var updateWrapper = function () {
|
||||||
|
|
||||||
intervalCount++;
|
intervalCount++;
|
||||||
var thisInterval = Date.now();
|
var thisInterval = Date.now();
|
||||||
|
@ -4139,12 +4142,12 @@ function updateWrapper(){
|
||||||
updateTotalWork = 0;
|
updateTotalWork = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.update.connect(updateWrapper);
|
Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS);
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
Menu.removeMenuItem("Developer", "Show Grab Sphere");
|
Menu.removeMenuItem("Developer", "Show Grab Sphere");
|
||||||
Script.update.disconnect(updateWrapper);
|
|
||||||
rightController.cleanup();
|
rightController.cleanup();
|
||||||
leftController.cleanup();
|
leftController.cleanup();
|
||||||
Controller.disableMapping(MAPPING_NAME);
|
Controller.disableMapping(MAPPING_NAME);
|
||||||
|
|
|
@ -158,7 +158,7 @@ input[type=button].naked:active {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: calc(100% - 60px);
|
width: calc(100% - 60px);
|
||||||
margin-bottom: 25px;
|
margin-bottom: -24px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
.shareButtons img {
|
.shareButtons img {
|
||||||
|
@ -178,7 +178,7 @@ input[type=button].naked:active {
|
||||||
height: 25px;
|
height: 25px;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 40px;
|
||||||
left: 73px;
|
left: 73px;
|
||||||
right: 0;
|
right: 0;
|
||||||
font-family: Raleway-Regular;
|
font-family: Raleway-Regular;
|
||||||
|
@ -187,6 +187,19 @@ input[type=button].naked:active {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
.helpTextDiv {
|
||||||
|
width: 350px;
|
||||||
|
height: 65px;
|
||||||
|
margin-right: 15px;
|
||||||
|
line-height: 65px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
font-family: Raleway-Regular;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
// END styling of share overlay
|
// END styling of share overlay
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -136,11 +136,14 @@ input[type=radio]:active + label > span > span{
|
||||||
}
|
}
|
||||||
|
|
||||||
.grayButton {
|
.grayButton {
|
||||||
font-family: FiraSans-SemiBold;
|
font-family: Raleway-Bold;
|
||||||
color: white;
|
font-size: 13px;
|
||||||
|
color: black;
|
||||||
padding: 0px 10px;
|
padding: 0px 10px;
|
||||||
|
border-radius: 3px;
|
||||||
border-width: 0px;
|
border-width: 0px;
|
||||||
background-image: linear-gradient(#FFFFFF, #AFAFAF);
|
background-image: linear-gradient(#FFFFFF, #AFAFAF);
|
||||||
|
min-height: 30px;
|
||||||
}
|
}
|
||||||
.grayButton:hover {
|
.grayButton:hover {
|
||||||
background-image: linear-gradient(#FFFFFF, #FFFFFF);
|
background-image: linear-gradient(#FFFFFF, #FFFFFF);
|
||||||
|
@ -152,7 +155,8 @@ input[type=radio]:active + label > span > span{
|
||||||
background-image: linear-gradient(#FFFFFF, ##AFAFAF);
|
background-image: linear-gradient(#FFFFFF, ##AFAFAF);
|
||||||
}
|
}
|
||||||
.blueButton {
|
.blueButton {
|
||||||
font-family: FiraSans-SemiBold;
|
font-family: Raleway-Bold;
|
||||||
|
font-size: 13px;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0px 10px;
|
padding: 0px 10px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
|
@ -62,6 +62,12 @@ function chooseSnapshotLocation() {
|
||||||
action: "chooseSnapshotLocation"
|
action: "chooseSnapshotLocation"
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
function login() {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "snapshot",
|
||||||
|
action: "login"
|
||||||
|
}));
|
||||||
|
}
|
||||||
function clearImages() {
|
function clearImages() {
|
||||||
document.getElementById("snap-button").disabled = false;
|
document.getElementById("snap-button").disabled = false;
|
||||||
var snapshotImagesDiv = document.getElementById("snapshot-images");
|
var snapshotImagesDiv = document.getElementById("snapshot-images");
|
||||||
|
@ -74,6 +80,52 @@ function clearImages() {
|
||||||
idCounter = 0;
|
idCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectImageWithHelpText(selectedID, isSelected) {
|
||||||
|
if (selectedID.id) {
|
||||||
|
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
|
||||||
|
}
|
||||||
|
var imageContainer = document.getElementById(selectedID),
|
||||||
|
image = document.getElementById(selectedID + 'img'),
|
||||||
|
shareBar = document.getElementById(selectedID + "shareBar"),
|
||||||
|
helpTextDiv = document.getElementById(selectedID + "helpTextDiv"),
|
||||||
|
showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"),
|
||||||
|
itr,
|
||||||
|
containers = document.getElementsByClassName("shareControls");
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
showShareButtonsButtonDiv.onclick = function () { selectImageWithHelpText(selectedID, false); };
|
||||||
|
showShareButtonsButtonDiv.classList.remove("inactive");
|
||||||
|
showShareButtonsButtonDiv.classList.add("active");
|
||||||
|
|
||||||
|
image.onclick = function () { selectImageWithHelpText(selectedID, false); };
|
||||||
|
imageContainer.style.outline = "4px solid #00b4ef";
|
||||||
|
imageContainer.style.outlineOffset = "-4px";
|
||||||
|
|
||||||
|
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)";
|
||||||
|
shareBar.style.pointerEvents = "initial";
|
||||||
|
|
||||||
|
helpTextDiv.style.visibility = "visible";
|
||||||
|
|
||||||
|
for (itr = 0; itr < containers.length; itr += 1) {
|
||||||
|
var parentID = containers[itr].id.slice(0, 2);
|
||||||
|
if (parentID !== selectedID) {
|
||||||
|
selectImageWithHelpText(parentID, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showShareButtonsButtonDiv.onclick = function () { selectImageWithHelpText(selectedID, true); };
|
||||||
|
showShareButtonsButtonDiv.classList.remove("active");
|
||||||
|
showShareButtonsButtonDiv.classList.add("inactive");
|
||||||
|
|
||||||
|
image.onclick = function () { selectImageWithHelpText(selectedID, true); };
|
||||||
|
imageContainer.style.outline = "none";
|
||||||
|
|
||||||
|
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)";
|
||||||
|
shareBar.style.pointerEvents = "none";
|
||||||
|
|
||||||
|
helpTextDiv.style.visibility = "hidden";
|
||||||
|
}
|
||||||
|
}
|
||||||
function selectImageToShare(selectedID, isSelected) {
|
function selectImageToShare(selectedID, isSelected) {
|
||||||
if (selectedID.id) {
|
if (selectedID.id) {
|
||||||
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
|
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
|
||||||
|
@ -97,6 +149,7 @@ function selectImageToShare(selectedID, isSelected) {
|
||||||
imageContainer.style.outlineOffset = "-4px";
|
imageContainer.style.outlineOffset = "-4px";
|
||||||
|
|
||||||
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)";
|
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)";
|
||||||
|
shareBar.style.pointerEvents = "initial";
|
||||||
|
|
||||||
shareButtonsDiv.style.visibility = "visible";
|
shareButtonsDiv.style.visibility = "visible";
|
||||||
shareBarHelp.style.visibility = "visible";
|
shareBarHelp.style.visibility = "visible";
|
||||||
|
@ -116,12 +169,13 @@ function selectImageToShare(selectedID, isSelected) {
|
||||||
imageContainer.style.outline = "none";
|
imageContainer.style.outline = "none";
|
||||||
|
|
||||||
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)";
|
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)";
|
||||||
|
shareBar.style.pointerEvents = "none";
|
||||||
|
|
||||||
shareButtonsDiv.style.visibility = "hidden";
|
shareButtonsDiv.style.visibility = "hidden";
|
||||||
shareBarHelp.style.visibility = "hidden";
|
shareBarHelp.style.visibility = "hidden";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) {
|
function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) {
|
||||||
var shareBar = document.createElement("div"),
|
var shareBar = document.createElement("div"),
|
||||||
shareBarHelpID = parentID + "shareBarHelp",
|
shareBarHelpID = parentID + "shareBarHelp",
|
||||||
shareButtonsDivID = parentID + "shareButtonsDiv",
|
shareButtonsDivID = parentID + "shareButtonsDiv",
|
||||||
|
@ -130,11 +184,16 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled
|
||||||
blastToConnectionsButtonID = parentID + "blastToConnectionsButton",
|
blastToConnectionsButtonID = parentID + "blastToConnectionsButton",
|
||||||
shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton",
|
shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton",
|
||||||
facebookButtonID = parentID + "facebookButton",
|
facebookButtonID = parentID + "facebookButton",
|
||||||
twitterButtonID = parentID + "twitterButton";
|
twitterButtonID = parentID + "twitterButton",
|
||||||
|
shareBarInnerHTML = '';
|
||||||
|
|
||||||
shareBar.id = parentID + "shareBar";
|
shareBar.id = parentID + "shareBar";
|
||||||
shareBar.className = "shareControls";
|
shareBar.className = "shareControls";
|
||||||
var shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
|
|
||||||
|
if (isLoggedIn) {
|
||||||
|
if (canShare) {
|
||||||
|
shareBarInnerHTML = '<div class="shareControlsHelp" id="' + shareBarHelpID + '" style="visibility:hidden;' + ((canBlast && blastButtonDisabled || !canBlast && hifiButtonDisabled) ? "background-color:#000;opacity:0.5;" : "") + '"></div>' +
|
||||||
|
'<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
|
||||||
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
|
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
|
||||||
'<span class="showShareButtonDots">' +
|
'<span class="showShareButtonDots">' +
|
||||||
'' +
|
'' +
|
||||||
|
@ -142,33 +201,67 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div class="shareButtons" id="' + shareButtonsDivID + '" style="visibility:hidden">';
|
'<div class="shareButtons" id="' + shareButtonsDivID + '" style="visibility:hidden">';
|
||||||
if (canBlast) {
|
if (canBlast) {
|
||||||
shareBarInnerHTML += '<div class="shareButton blastToConnections' + (blastButtonDisabled ? ' disabled' : '') + '" id="' + blastToConnectionsButtonID + '" onmouseover="shareButtonHovered(\'blast\', ' + parentID + ')" onclick="' + (blastButtonDisabled ? '' : 'blastToConnections(' + parentID + ', ' + isGif + ')') + '"><img src="img/blast_icon.svg"></div>';
|
shareBarInnerHTML += '<div class="shareButton blastToConnections' + (blastButtonDisabled ? ' disabled' : '') + '" id="' + blastToConnectionsButtonID + '" onmouseover="shareButtonHovered(\'blast\', ' + parentID + ', true)" onclick="' + (blastButtonDisabled ? '' : 'blastToConnections(' + parentID + ', ' + isGif + ')') + '"><img src="img/blast_icon.svg"></div>';
|
||||||
}
|
}
|
||||||
shareBarInnerHTML += '<div class="shareButton shareWithEveryone' + (hifiButtonDisabled ? ' disabled' : '') + '" id="' + shareWithEveryoneButtonID + '" onmouseover="shareButtonHovered(\'hifi\', ' + parentID + ')" onclick="' + (hifiButtonDisabled ? '' : 'shareWithEveryone(' + parentID + ', ' + isGif + ')') + '"><img src="img/hifi_icon.svg" style="width:35px;height:35px;margin:2px 0 0 2px;"></div>' +
|
shareBarInnerHTML += '<div class="shareButton shareWithEveryone' + (hifiButtonDisabled ? ' disabled' : '') + '" id="' + shareWithEveryoneButtonID + '" onmouseover="shareButtonHovered(\'hifi\', ' + parentID + ', true)" onclick="' + (hifiButtonDisabled ? '' : 'shareWithEveryone(' + parentID + ', ' + isGif + ')') + '"><img src="img/hifi_icon.svg" style="width:35px;height:35px;margin:2px 0 0 2px;"></div>' +
|
||||||
'<a class="shareButton facebookButton" id="' + facebookButtonID + '" onmouseover="shareButtonHovered(\'facebook\', ' + parentID + ')" onclick="shareButtonClicked(\'facebook\', ' + parentID + ')"><img src="img/fb_icon.svg"></a>' +
|
'<a class="shareButton facebookButton" id="' + facebookButtonID + '" onmouseover="shareButtonHovered(\'facebook\', ' + parentID + ', true)" onclick="shareButtonClicked(\'facebook\', ' + parentID + ')"><img src="img/fb_icon.svg"></a>' +
|
||||||
'<a class="shareButton twitterButton" id="' + twitterButtonID + '" onmouseover="shareButtonHovered(\'twitter\', ' + parentID + ')" onclick="shareButtonClicked(\'twitter\', ' + parentID + ')"><img src="img/twitter_icon.svg"></a>' +
|
'<a class="shareButton twitterButton" id="' + twitterButtonID + '" onmouseover="shareButtonHovered(\'twitter\', ' + parentID + ', true)" onclick="shareButtonClicked(\'twitter\', ' + parentID + ')"><img src="img/twitter_icon.svg"></a>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
|
||||||
shareBar.innerHTML = shareBarInnerHTML;
|
|
||||||
|
|
||||||
shareBar.innerHTML += '<div class="shareControlsHelp" id="' + shareBarHelpID + '" style="visibility:hidden;' + ((canBlast && blastButtonDisabled || !canBlast && hifiButtonDisabled) ? "background-color:#000;opacity:0.5;" : "") + '"></div>';
|
|
||||||
|
|
||||||
// Add onclick handler to parent DIV's img to toggle share buttons
|
// Add onclick handler to parent DIV's img to toggle share buttons
|
||||||
document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); };
|
document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); };
|
||||||
|
} else {
|
||||||
|
shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
|
||||||
|
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
|
||||||
|
'<span class="showShareButtonDots">' +
|
||||||
|
'' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="helpTextDiv" id="' + parentID + 'helpTextDiv' + '" style="visibility:hidden;text-align:left;">' +
|
||||||
|
'This snap was taken in an unshareable domain.' +
|
||||||
|
'</div>';
|
||||||
|
// Add onclick handler to parent DIV's img to toggle share buttons
|
||||||
|
document.getElementById(parentID + 'img').onclick = function () { selectImageWithHelpText(parentID, true); };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
|
||||||
|
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
|
||||||
|
'<span class="showShareButtonDots">' +
|
||||||
|
'' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="helpTextDiv" id="' + parentID + 'helpTextDiv' + '" style="visibility:hidden;text-align:right;">' +
|
||||||
|
'Please log in to share snaps' + '<input class="grayButton" style="margin-left:20px;width:95px;height:30px;" type="button" value="LOG IN" onclick="login()" />' +
|
||||||
|
'</div>';
|
||||||
|
// Add onclick handler to parent DIV's img to toggle share buttons
|
||||||
|
document.getElementById(parentID + 'img').onclick = function () { selectImageWithHelpText(parentID, true); };
|
||||||
|
}
|
||||||
|
|
||||||
|
shareBar.innerHTML = shareBarInnerHTML;
|
||||||
|
|
||||||
return shareBar;
|
return shareBar;
|
||||||
}
|
}
|
||||||
function appendShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) {
|
function appendShareBar(divID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) {
|
||||||
if (divID.id) {
|
if (divID.id) {
|
||||||
divID = divID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
|
divID = divID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
|
||||||
}
|
}
|
||||||
document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast));
|
document.getElementById(divID).appendChild(createShareBar(divID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast));
|
||||||
if (divID === "p0") {
|
if (divID === "p0") {
|
||||||
|
if (isLoggedIn) {
|
||||||
|
if (canShare) {
|
||||||
selectImageToShare(divID, true);
|
selectImageToShare(divID, true);
|
||||||
if (canBlast) {
|
|
||||||
shareButtonHovered('blast', divID);
|
|
||||||
} else {
|
} else {
|
||||||
shareButtonHovered('hifi', divID);
|
selectImageWithHelpText(divID, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectImageWithHelpText(divID, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isLoggedIn && canShare) {
|
||||||
|
if (canBlast) {
|
||||||
|
shareButtonHovered('blast', divID, false);
|
||||||
|
} else {
|
||||||
|
shareButtonHovered('hifi', divID, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +272,7 @@ function shareForUrl(selectedID) {
|
||||||
data: paths[parseInt(selectedID.substring(1), 10)]
|
data: paths[parseInt(selectedID.substring(1), 10)]
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) {
|
function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) {
|
||||||
if (!image_data.localPath) {
|
if (!image_data.localPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -204,12 +297,13 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b
|
||||||
if (isGif) {
|
if (isGif) {
|
||||||
imageContainer.innerHTML += '<span class="gifLabel">GIF</span>';
|
imageContainer.innerHTML += '<span class="gifLabel">GIF</span>';
|
||||||
}
|
}
|
||||||
if (!isGifLoading && !isShowingPreviousImages && canShare) {
|
if (!isGifLoading) {
|
||||||
appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, true);
|
appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast);
|
||||||
|
}
|
||||||
|
if (!isGifLoading && !isShowingPreviousImages) {
|
||||||
shareForUrl(id);
|
shareForUrl(id);
|
||||||
}
|
}
|
||||||
if (isShowingPreviousImages && image_data.story_id) {
|
if (isShowingPreviousImages && isLoggedIn && image_data.story_id) {
|
||||||
appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast);
|
|
||||||
updateShareInfo(id, image_data.story_id);
|
updateShareInfo(id, image_data.story_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,6 +356,7 @@ function showUploadingMessage(selectedID, destination) {
|
||||||
var shareBarHelp = document.getElementById(selectedID + "shareBarHelp");
|
var shareBarHelp = document.getElementById(selectedID + "shareBarHelp");
|
||||||
|
|
||||||
shareBarHelp.innerHTML = '<img style="display:inline;width:25px;height:25px;" src="./img/loader.gif"></img><span style="position:relative;margin-left:5px;bottom:7px;">Preparing to Share</span>';
|
shareBarHelp.innerHTML = '<img style="display:inline;width:25px;height:25px;" src="./img/loader.gif"></img><span style="position:relative;margin-left:5px;bottom:7px;">Preparing to Share</span>';
|
||||||
|
shareBarHelp.classList.add("uploading");
|
||||||
shareBarHelp.setAttribute("data-destination", destination);
|
shareBarHelp.setAttribute("data-destination", destination);
|
||||||
}
|
}
|
||||||
function hideUploadingMessageAndShare(selectedID, storyID) {
|
function hideUploadingMessageAndShare(selectedID, storyID) {
|
||||||
|
@ -271,6 +366,8 @@ function hideUploadingMessageAndShare(selectedID, storyID) {
|
||||||
|
|
||||||
var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"),
|
var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"),
|
||||||
shareBarHelpDestination = shareBarHelp.getAttribute("data-destination");
|
shareBarHelpDestination = shareBarHelp.getAttribute("data-destination");
|
||||||
|
|
||||||
|
shareBarHelp.classList.remove("uploading");
|
||||||
if (shareBarHelpDestination) {
|
if (shareBarHelpDestination) {
|
||||||
switch (shareBarHelpDestination) {
|
switch (shareBarHelpDestination) {
|
||||||
case 'blast':
|
case 'blast':
|
||||||
|
@ -380,7 +477,7 @@ function shareWithEveryone(selectedID, isGif) {
|
||||||
showUploadingMessage(selectedID, 'hifi');
|
showUploadingMessage(selectedID, 'hifi');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function shareButtonHovered(destination, selectedID) {
|
function shareButtonHovered(destination, selectedID, shouldAlsoModifyOther) {
|
||||||
if (selectedID.id) {
|
if (selectedID.id) {
|
||||||
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
|
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
|
||||||
}
|
}
|
||||||
|
@ -388,11 +485,11 @@ function shareButtonHovered(destination, selectedID) {
|
||||||
shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv").childNodes,
|
shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv").childNodes,
|
||||||
itr;
|
itr;
|
||||||
|
|
||||||
|
if (!shareBarHelp.classList.contains("uploading")) {
|
||||||
for (itr = 0; itr < shareButtonsDiv.length; itr += 1) {
|
for (itr = 0; itr < shareButtonsDiv.length; itr += 1) {
|
||||||
shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)";
|
shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)";
|
||||||
}
|
}
|
||||||
shareBarHelp.style.opacity = "1.0";
|
shareBarHelp.style.opacity = "1.0";
|
||||||
|
|
||||||
switch (destination) {
|
switch (destination) {
|
||||||
case 'blast':
|
case 'blast':
|
||||||
var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton");
|
var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton");
|
||||||
|
@ -437,6 +534,15 @@ function shareButtonHovered(destination, selectedID) {
|
||||||
document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE";
|
document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldAlsoModifyOther && imageCount > 1) {
|
||||||
|
if (selectedID === "p0" && !document.getElementById("p1").classList.contains("processingGif")) {
|
||||||
|
shareButtonHovered(destination, "p1", false);
|
||||||
|
} else if (selectedID === "p1") {
|
||||||
|
shareButtonHovered(destination, "p0", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function shareButtonClicked(destination, selectedID) {
|
function shareButtonClicked(destination, selectedID) {
|
||||||
if (selectedID.id) {
|
if (selectedID.id) {
|
||||||
|
@ -474,7 +580,7 @@ function handleCaptureSetting(setting) {
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
// Uncomment the line below to test functionality in a browser.
|
// Uncomment the line below to test functionality in a browser.
|
||||||
// See definition of "testInBrowser()" to modify tests.
|
// See definition of "testInBrowser()" to modify tests.
|
||||||
//testInBrowser(3);
|
//testInBrowser(4);
|
||||||
openEventBridge(function () {
|
openEventBridge(function () {
|
||||||
// Set up a handler for receiving the data, and tell the .js we are ready to receive it.
|
// Set up a handler for receiving the data, and tell the .js we are ready to receive it.
|
||||||
EventBridge.scriptEventReceived.connect(function (message) {
|
EventBridge.scriptEventReceived.connect(function (message) {
|
||||||
|
@ -503,7 +609,7 @@ window.onload = function () {
|
||||||
imageCount = message.image_data.length;
|
imageCount = message.image_data.length;
|
||||||
if (imageCount > 0) {
|
if (imageCount > 0) {
|
||||||
message.image_data.forEach(function (element, idx) {
|
message.image_data.forEach(function (element, idx) {
|
||||||
addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast);
|
addImage(element, messageOptions.isLoggedIn, message.canShare, false, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
showSnapshotInstructions();
|
showSnapshotInstructions();
|
||||||
|
@ -519,23 +625,23 @@ window.onload = function () {
|
||||||
imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon
|
imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon
|
||||||
message.image_data.push({ localPath: messageOptions.loadingGifPath });
|
message.image_data.push({ localPath: messageOptions.loadingGifPath });
|
||||||
message.image_data.forEach(function (element, idx) {
|
message.image_data.forEach(function (element, idx) {
|
||||||
addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false);
|
addImage(element, messageOptions.isLoggedIn, idx === 0 && messageOptions.canShare, idx === 1, false, false, false, true);
|
||||||
});
|
});
|
||||||
|
document.getElementById("p1").classList.add("processingGif");
|
||||||
} else {
|
} else {
|
||||||
var gifPath = message.image_data[0].localPath,
|
var gifPath = message.image_data[0].localPath,
|
||||||
p1img = document.getElementById('p1img');
|
p1img = document.getElementById('p1img');
|
||||||
p1img.src = gifPath;
|
p1img.src = gifPath;
|
||||||
|
|
||||||
paths[1] = gifPath;
|
paths[1] = gifPath;
|
||||||
if (messageOptions.canShare) {
|
|
||||||
shareForUrl("p1");
|
shareForUrl("p1");
|
||||||
appendShareBar("p1", true, false, false, true);
|
appendShareBar("p1", messageOptions.isLoggedIn, messageOptions.canShare, true, false, false, messageOptions.canBlast);
|
||||||
}
|
document.getElementById("p1").classList.remove("processingGif");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
imageCount = message.image_data.length;
|
imageCount = message.image_data.length;
|
||||||
message.image_data.forEach(function (element) {
|
message.image_data.forEach(function (element) {
|
||||||
addImage(element, false, messageOptions.canShare, false);
|
addImage(element, messageOptions.isLoggedIn, messageOptions.canShare, false, false, false, false, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -577,18 +683,23 @@ function testInBrowser(test) {
|
||||||
} else if (test === 1) {
|
} else if (test === 1) {
|
||||||
imageCount = 2;
|
imageCount = 2;
|
||||||
//addImage({ localPath: 'http://lorempixel.com/553/255' });
|
//addImage({ localPath: 'http://lorempixel.com/553/255' });
|
||||||
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true);
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true);
|
||||||
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true);
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true);
|
||||||
} else if (test === 2) {
|
} else if (test === 2) {
|
||||||
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true);
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true);
|
||||||
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true);
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true);
|
||||||
showConfirmationMessage("p0", 'blast');
|
showConfirmationMessage("p0", 'blast');
|
||||||
showConfirmationMessage("p1", 'hifi');
|
showConfirmationMessage("p1", 'hifi');
|
||||||
} else if (test === 3) {
|
} else if (test === 3) {
|
||||||
imageCount = 2;
|
imageCount = 2;
|
||||||
//addImage({ localPath: 'http://lorempixel.com/553/255' });
|
//addImage({ localPath: 'http://lorempixel.com/553/255' });
|
||||||
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true);
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true);
|
||||||
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true);
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true);
|
||||||
showUploadingMessage("p0", 'hifi');
|
showUploadingMessage("p0", 'hifi');
|
||||||
}
|
} else if (test === 4) {
|
||||||
|
imageCount = 2;
|
||||||
|
//addImage({ localPath: 'http://lorempixel.com/553/255' });
|
||||||
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, false, true, false, false, true);
|
||||||
|
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, false, true, false, false, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location) {
|
||||||
modelURL: modelURL,
|
modelURL: modelURL,
|
||||||
url: modelURL, // for overlay
|
url: modelURL, // for overlay
|
||||||
grabbable: true, // for overlay
|
grabbable: true, // for overlay
|
||||||
|
loadPriority: 10.0, // for overlay
|
||||||
userData: JSON.stringify({
|
userData: JSON.stringify({
|
||||||
"grabbableKey": {"grabbable": true}
|
"grabbableKey": {"grabbable": true}
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -29,63 +29,17 @@ var button = tablet.addButton({
|
||||||
sortOrder: 5
|
sortOrder: 5
|
||||||
});
|
});
|
||||||
|
|
||||||
var snapshotOptions;
|
var snapshotOptions = {};
|
||||||
var imageData = [];
|
var imageData = [];
|
||||||
var storyIDsToMaybeDelete = [];
|
var storyIDsToMaybeDelete = [];
|
||||||
var shareAfterLogin = false;
|
var shareAfterLogin = false;
|
||||||
var snapshotToShareAfterLogin;
|
var snapshotToShareAfterLogin = [];
|
||||||
var METAVERSE_BASE = location.metaverseServerUrl;
|
var METAVERSE_BASE = location.metaverseServerUrl;
|
||||||
|
var isLoggedIn;
|
||||||
|
|
||||||
// It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story,
|
// It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story,
|
||||||
// POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS
|
// POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS
|
||||||
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
|
var request = Script.require('request').request;
|
||||||
var httpRequest = new XMLHttpRequest(), key;
|
|
||||||
// QT bug: apparently doesn't handle onload. Workaround using readyState.
|
|
||||||
httpRequest.onreadystatechange = function () {
|
|
||||||
var READY_STATE_DONE = 4;
|
|
||||||
var HTTP_OK = 200;
|
|
||||||
if (httpRequest.readyState >= READY_STATE_DONE) {
|
|
||||||
var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText,
|
|
||||||
response = !error && httpRequest.responseText,
|
|
||||||
contentType = !error && httpRequest.getResponseHeader('content-type');
|
|
||||||
if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc.
|
|
||||||
try {
|
|
||||||
response = JSON.parse(response);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback(error, response);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (typeof options === 'string') {
|
|
||||||
options = { uri: options };
|
|
||||||
}
|
|
||||||
if (options.url) {
|
|
||||||
options.uri = options.url;
|
|
||||||
}
|
|
||||||
if (!options.method) {
|
|
||||||
options.method = 'GET';
|
|
||||||
}
|
|
||||||
if (options.body && (options.method === 'GET')) { // add query parameters
|
|
||||||
var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&';
|
|
||||||
for (key in options.body) {
|
|
||||||
params.push(key + '=' + options.body[key]);
|
|
||||||
}
|
|
||||||
options.uri += appender + params.join('&');
|
|
||||||
delete options.body;
|
|
||||||
}
|
|
||||||
if (options.json) {
|
|
||||||
options.headers = options.headers || {};
|
|
||||||
options.headers["Content-type"] = "application/json";
|
|
||||||
options.body = JSON.stringify(options.body);
|
|
||||||
}
|
|
||||||
for (key in options.headers || {}) {
|
|
||||||
httpRequest.setRequestHeader(key, options.headers[key]);
|
|
||||||
}
|
|
||||||
httpRequest.open(options.method, options.uri, true);
|
|
||||||
httpRequest.send(options.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openLoginWindow() {
|
function openLoginWindow() {
|
||||||
if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false))
|
if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false))
|
||||||
|
@ -108,7 +62,6 @@ function onMessage(message) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isLoggedIn;
|
|
||||||
switch (message.action) {
|
switch (message.action) {
|
||||||
case 'ready': // DOM is ready and page has loaded
|
case 'ready': // DOM is ready and page has loaded
|
||||||
tablet.emitScriptEvent(JSON.stringify({
|
tablet.emitScriptEvent(JSON.stringify({
|
||||||
|
@ -141,6 +94,9 @@ function onMessage(message) {
|
||||||
Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);
|
Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'login':
|
||||||
|
openLoginWindow();
|
||||||
|
break;
|
||||||
case 'chooseSnapshotLocation':
|
case 'chooseSnapshotLocation':
|
||||||
var snapshotPath = Window.browseDir("Choose Snapshots Directory", "", "");
|
var snapshotPath = Window.browseDir("Choose Snapshots Directory", "", "");
|
||||||
|
|
||||||
|
@ -172,23 +128,28 @@ function onMessage(message) {
|
||||||
takeSnapshot();
|
takeSnapshot();
|
||||||
break;
|
break;
|
||||||
case 'shareSnapshotForUrl':
|
case 'shareSnapshotForUrl':
|
||||||
|
isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) {
|
||||||
|
if (canShare) {
|
||||||
isLoggedIn = Account.isLoggedIn();
|
isLoggedIn = Account.isLoggedIn();
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
print('Sharing snapshot with audience "for_url":', message.data);
|
print('Sharing snapshot with audience "for_url":', message.data);
|
||||||
Window.shareSnapshot(message.data, message.href || href);
|
Window.shareSnapshot(message.data, message.href || href);
|
||||||
} else {
|
} else {
|
||||||
// TODO
|
shareAfterLogin = true;
|
||||||
|
snapshotToShareAfterLogin.push({ path: message.data, href: message.href || href });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 'blastToConnections':
|
case 'blastToConnections':
|
||||||
isLoggedIn = Account.isLoggedIn();
|
isLoggedIn = Account.isLoggedIn();
|
||||||
|
if (isLoggedIn) {
|
||||||
if (message.isGif) {
|
if (message.isGif) {
|
||||||
Settings.setValue("previousAnimatedSnapBlastingDisabled", true);
|
Settings.setValue("previousAnimatedSnapBlastingDisabled", true);
|
||||||
} else {
|
} else {
|
||||||
Settings.setValue("previousStillSnapBlastingDisabled", true);
|
Settings.setValue("previousStillSnapBlastingDisabled", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoggedIn) {
|
|
||||||
print('Uploading new story for announcement!');
|
print('Uploading new story for announcement!');
|
||||||
|
|
||||||
request({
|
request({
|
||||||
|
@ -234,20 +195,16 @@ function onMessage(message) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
|
||||||
openLoginWindow();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'shareSnapshotWithEveryone':
|
case 'shareSnapshotWithEveryone':
|
||||||
isLoggedIn = Account.isLoggedIn();
|
isLoggedIn = Account.isLoggedIn();
|
||||||
|
if (isLoggedIn) {
|
||||||
if (message.isGif) {
|
if (message.isGif) {
|
||||||
Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true);
|
Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true);
|
||||||
} else {
|
} else {
|
||||||
Settings.setValue("previousStillSnapHifiSharingDisabled", true);
|
Settings.setValue("previousStillSnapHifiSharingDisabled", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoggedIn) {
|
|
||||||
print('Modifying audience of story ID', message.story_id, "to 'for_feed'");
|
print('Modifying audience of story ID', message.story_id, "to 'for_feed'");
|
||||||
var requestBody = {
|
var requestBody = {
|
||||||
audience: "for_feed"
|
audience: "for_feed"
|
||||||
|
@ -275,10 +232,6 @@ function onMessage(message) {
|
||||||
print("SUCCESS changing audience" + (message.isAnnouncement ? " and posting announcement!" : "!"));
|
print("SUCCESS changing audience" + (message.isAnnouncement ? " and posting announcement!" : "!"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
openLoginWindow();
|
|
||||||
shareAfterLogin = true;
|
|
||||||
snapshotToShareAfterLogin = { path: message.data, href: message.href || href };
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'removeFromStoryIDsToMaybeDelete':
|
case 'removeFromStoryIDsToMaybeDelete':
|
||||||
|
@ -291,15 +244,8 @@ function onMessage(message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html");
|
function fillImageDataFromPrevious() {
|
||||||
var isInSnapshotReview = false;
|
isLoggedIn = Account.isLoggedIn();
|
||||||
var shouldActivateButton = false;
|
|
||||||
function onButtonClicked() {
|
|
||||||
if (isInSnapshotReview){
|
|
||||||
// for toolbar-mode: go back to home screen, this will close the window.
|
|
||||||
tablet.gotoHomeScreen();
|
|
||||||
} else {
|
|
||||||
shouldActivateButton = true;
|
|
||||||
var previousStillSnapPath = Settings.getValue("previousStillSnapPath");
|
var previousStillSnapPath = Settings.getValue("previousStillSnapPath");
|
||||||
var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID");
|
var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID");
|
||||||
var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled");
|
var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled");
|
||||||
|
@ -312,8 +258,9 @@ function onButtonClicked() {
|
||||||
containsGif: previousAnimatedSnapPath !== "",
|
containsGif: previousAnimatedSnapPath !== "",
|
||||||
processingGif: false,
|
processingGif: false,
|
||||||
shouldUpload: false,
|
shouldUpload: false,
|
||||||
canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID")
|
canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"),
|
||||||
}
|
isLoggedIn: isLoggedIn
|
||||||
|
};
|
||||||
imageData = [];
|
imageData = [];
|
||||||
if (previousStillSnapPath !== "") {
|
if (previousStillSnapPath !== "") {
|
||||||
imageData.push({
|
imageData.push({
|
||||||
|
@ -331,6 +278,18 @@ function onButtonClicked() {
|
||||||
hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled
|
hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html");
|
||||||
|
var isInSnapshotReview = false;
|
||||||
|
var shouldActivateButton = false;
|
||||||
|
function onButtonClicked() {
|
||||||
|
if (isInSnapshotReview){
|
||||||
|
// for toolbar-mode: go back to home screen, this will close the window.
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
} else {
|
||||||
|
shouldActivateButton = true;
|
||||||
|
fillImageDataFromPrevious();
|
||||||
tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL);
|
tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL);
|
||||||
tablet.webEventReceived.connect(onMessage);
|
tablet.webEventReceived.connect(onMessage);
|
||||||
HMD.openTablet();
|
HMD.openTablet();
|
||||||
|
@ -453,6 +412,7 @@ function isDomainOpen(id, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function stillSnapshotTaken(pathStillSnapshot, notify) {
|
function stillSnapshotTaken(pathStillSnapshot, notify) {
|
||||||
|
isLoggedIn = Account.isLoggedIn();
|
||||||
// show hud
|
// show hud
|
||||||
Reticle.visible = reticleVisible;
|
Reticle.visible = reticleVisible;
|
||||||
Reticle.allowMouseCapture = true;
|
Reticle.allowMouseCapture = true;
|
||||||
|
@ -481,7 +441,8 @@ function stillSnapshotTaken(pathStillSnapshot, notify) {
|
||||||
snapshotOptions = {
|
snapshotOptions = {
|
||||||
containsGif: false,
|
containsGif: false,
|
||||||
processingGif: false,
|
processingGif: false,
|
||||||
canShare: canShare
|
canShare: canShare,
|
||||||
|
isLoggedIn: isLoggedIn
|
||||||
};
|
};
|
||||||
imageData = [{ localPath: pathStillSnapshot, href: href }];
|
imageData = [{ localPath: pathStillSnapshot, href: href }];
|
||||||
tablet.emitScriptEvent(JSON.stringify({
|
tablet.emitScriptEvent(JSON.stringify({
|
||||||
|
@ -496,6 +457,7 @@ function stillSnapshotTaken(pathStillSnapshot, notify) {
|
||||||
function processingGifStarted(pathStillSnapshot) {
|
function processingGifStarted(pathStillSnapshot) {
|
||||||
Window.processingGifStarted.disconnect(processingGifStarted);
|
Window.processingGifStarted.disconnect(processingGifStarted);
|
||||||
Window.processingGifCompleted.connect(processingGifCompleted);
|
Window.processingGifCompleted.connect(processingGifCompleted);
|
||||||
|
isLoggedIn = Account.isLoggedIn();
|
||||||
// show hud
|
// show hud
|
||||||
Reticle.visible = reticleVisible;
|
Reticle.visible = reticleVisible;
|
||||||
Reticle.allowMouseCapture = true;
|
Reticle.allowMouseCapture = true;
|
||||||
|
@ -515,7 +477,8 @@ function processingGifStarted(pathStillSnapshot) {
|
||||||
containsGif: true,
|
containsGif: true,
|
||||||
processingGif: true,
|
processingGif: true,
|
||||||
loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'),
|
loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'),
|
||||||
canShare: canShare
|
canShare: canShare,
|
||||||
|
isLoggedIn: isLoggedIn
|
||||||
};
|
};
|
||||||
imageData = [{ localPath: pathStillSnapshot, href: href }];
|
imageData = [{ localPath: pathStillSnapshot, href: href }];
|
||||||
tablet.emitScriptEvent(JSON.stringify({
|
tablet.emitScriptEvent(JSON.stringify({
|
||||||
|
@ -528,6 +491,7 @@ function processingGifStarted(pathStillSnapshot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function processingGifCompleted(pathAnimatedSnapshot) {
|
function processingGifCompleted(pathAnimatedSnapshot) {
|
||||||
|
isLoggedIn = Account.isLoggedIn();
|
||||||
Window.processingGifCompleted.disconnect(processingGifCompleted);
|
Window.processingGifCompleted.disconnect(processingGifCompleted);
|
||||||
if (!buttonConnected) {
|
if (!buttonConnected) {
|
||||||
button.clicked.connect(onButtonClicked);
|
button.clicked.connect(onButtonClicked);
|
||||||
|
@ -540,7 +504,9 @@ function processingGifCompleted(pathAnimatedSnapshot) {
|
||||||
snapshotOptions = {
|
snapshotOptions = {
|
||||||
containsGif: true,
|
containsGif: true,
|
||||||
processingGif: false,
|
processingGif: false,
|
||||||
canShare: canShare
|
canShare: canShare,
|
||||||
|
isLoggedIn: isLoggedIn,
|
||||||
|
canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"),
|
||||||
};
|
};
|
||||||
imageData = [{ localPath: pathAnimatedSnapshot, href: href }];
|
imageData = [{ localPath: pathAnimatedSnapshot, href: href }];
|
||||||
tablet.emitScriptEvent(JSON.stringify({
|
tablet.emitScriptEvent(JSON.stringify({
|
||||||
|
@ -576,10 +542,30 @@ function onTabletScreenChanged(type, url) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function onUsernameChanged() {
|
function onUsernameChanged() {
|
||||||
if (shareAfterLogin && Account.isLoggedIn()) {
|
fillImageDataFromPrevious();
|
||||||
print('Sharing snapshot after login:', snapshotToShareAfterLogin.path);
|
isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) {
|
||||||
Window.shareSnapshot(snapshotToShareAfterLogin.path, snapshotToShareAfterLogin.href);
|
tablet.emitScriptEvent(JSON.stringify({
|
||||||
|
type: "snapshot",
|
||||||
|
action: "showPreviousImages",
|
||||||
|
options: snapshotOptions,
|
||||||
|
image_data: imageData,
|
||||||
|
canShare: canShare
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
if (isLoggedIn) {
|
||||||
|
if (shareAfterLogin) {
|
||||||
|
isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) {
|
||||||
|
if (canShare) {
|
||||||
|
snapshotToShareAfterLogin.forEach(function (element) {
|
||||||
|
print('Uploading snapshot after login:', element.path);
|
||||||
|
Window.shareSnapshot(element.path, element.href);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
shareAfterLogin = false;
|
shareAfterLogin = false;
|
||||||
|
snapshotToShareAfterLogin = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function snapshotLocationSet(location) {
|
function snapshotLocationSet(location) {
|
||||||
|
@ -595,7 +581,7 @@ button.clicked.connect(onButtonClicked);
|
||||||
buttonConnected = true;
|
buttonConnected = true;
|
||||||
Window.snapshotShared.connect(snapshotUploaded);
|
Window.snapshotShared.connect(snapshotUploaded);
|
||||||
tablet.screenChanged.connect(onTabletScreenChanged);
|
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||||
Account.usernameChanged.connect(onUsernameChanged);
|
GlobalServices.myUsernameChanged.connect(onUsernameChanged);
|
||||||
Snapshot.snapshotLocationSet.connect(snapshotLocationSet);
|
Snapshot.snapshotLocationSet.connect(snapshotLocationSet);
|
||||||
Script.scriptEnding.connect(function () {
|
Script.scriptEnding.connect(function () {
|
||||||
if (buttonConnected) {
|
if (buttonConnected) {
|
||||||
|
|
|
@ -16,6 +16,13 @@
|
||||||
(function () { // BEGIN LOCAL_SCOPE
|
(function () { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
var request = Script.require('request').request;
|
var request = Script.require('request').request;
|
||||||
|
var DEBUG = false;
|
||||||
|
function debug() {
|
||||||
|
if (!DEBUG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
print('tablet-goto.js:', [].map.call(arguments, JSON.stringify));
|
||||||
|
}
|
||||||
|
|
||||||
var gotoQmlSource = "TabletAddressDialog.qml";
|
var gotoQmlSource = "TabletAddressDialog.qml";
|
||||||
var buttonName = "GOTO";
|
var buttonName = "GOTO";
|
||||||
|
@ -39,6 +46,7 @@
|
||||||
switch (message.method) {
|
switch (message.method) {
|
||||||
case 'request':
|
case 'request':
|
||||||
request(message.params, function (error, data) {
|
request(message.params, function (error, data) {
|
||||||
|
debug('rpc', request, 'error:', error, 'data:', data);
|
||||||
response.error = error;
|
response.error = error;
|
||||||
response.result = data;
|
response.result = data;
|
||||||
tablet.sendToQml(response);
|
tablet.sendToQml(response);
|
||||||
|
@ -100,10 +108,24 @@
|
||||||
button.clicked.connect(onClicked);
|
button.clicked.connect(onClicked);
|
||||||
tablet.screenChanged.connect(onScreenChanged);
|
tablet.screenChanged.connect(onScreenChanged);
|
||||||
|
|
||||||
var stories = {};
|
var stories = {}, pingPong = false;
|
||||||
var DEBUG = false;
|
function expire(id) {
|
||||||
|
var options = {
|
||||||
|
uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id,
|
||||||
|
method: 'PUT',
|
||||||
|
json: true,
|
||||||
|
body: {expire: "true"}
|
||||||
|
};
|
||||||
|
request(options, function (error, response) {
|
||||||
|
debug('expired story', options, 'error:', error, 'response:', response);
|
||||||
|
if (error || (response.status !== 'success')) {
|
||||||
|
print("ERROR expiring story: ", error || response.status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
function pollForAnnouncements() {
|
function pollForAnnouncements() {
|
||||||
var actions = DEBUG ? 'snapshot' : 'announcement';
|
// We could bail now if !Account.isLoggedIn(), but what if we someday have system-wide announcments?
|
||||||
|
var actions = 'announcement';
|
||||||
var count = DEBUG ? 10 : 100;
|
var count = DEBUG ? 10 : 100;
|
||||||
var options = [
|
var options = [
|
||||||
'now=' + new Date().toISOString(),
|
'now=' + new Date().toISOString(),
|
||||||
|
@ -117,13 +139,24 @@
|
||||||
request({
|
request({
|
||||||
uri: url
|
uri: url
|
||||||
}, function (error, data) {
|
}, function (error, data) {
|
||||||
|
debug(url, error, data);
|
||||||
if (error || (data.status !== 'success')) {
|
if (error || (data.status !== 'success')) {
|
||||||
print("Error: unable to get", url, error || data.status);
|
print("Error: unable to get", url, error || data.status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var didNotify = false;
|
var didNotify = false, key;
|
||||||
|
pingPong = !pingPong;
|
||||||
data.user_stories.forEach(function (story) {
|
data.user_stories.forEach(function (story) {
|
||||||
if (stories[story.id]) { // already seen
|
var stored = stories[story.id], storedOrNew = stored || story;
|
||||||
|
debug('story exists:', !!stored, storedOrNew);
|
||||||
|
if ((storedOrNew.username === Account.username) && (storedOrNew.place_name !== location.placename)) {
|
||||||
|
if (storedOrNew.audience == 'for_connections') { // Only expire if we haven't already done so.
|
||||||
|
expire(story.id);
|
||||||
|
}
|
||||||
|
return; // before marking
|
||||||
|
}
|
||||||
|
storedOrNew.pingPong = pingPong;
|
||||||
|
if (stored) { // already seen
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stories[story.id] = story;
|
stories[story.id] = story;
|
||||||
|
@ -131,12 +164,20 @@
|
||||||
Window.displayAnnouncement(message);
|
Window.displayAnnouncement(message);
|
||||||
didNotify = true;
|
didNotify = true;
|
||||||
});
|
});
|
||||||
|
for (key in stories) { // Any story we were tracking that was not marked, has expired.
|
||||||
|
if (stories[key].pingPong !== pingPong) {
|
||||||
|
debug('removing story', key);
|
||||||
|
delete stories[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
if (didNotify) {
|
if (didNotify) {
|
||||||
messagesWaiting(true);
|
messagesWaiting(true);
|
||||||
if (HMD.isHandControllerAvailable()) {
|
if (HMD.isHandControllerAvailable()) {
|
||||||
var STRENGTH = 1.0, DURATION_MS = 60, HAND = 2; // both hands
|
var STRENGTH = 1.0, DURATION_MS = 60, HAND = 2; // both hands
|
||||||
Controller.triggerHapticPulse(STRENGTH, DURATION_MS, HAND);
|
Controller.triggerHapticPulse(STRENGTH, DURATION_MS, HAND);
|
||||||
}
|
}
|
||||||
|
} else if (!Object.keys(stories).length) { // If there's nothing being tracked, then any messageWaiting has expired.
|
||||||
|
messagesWaiting(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays,
|
/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays,
|
||||||
MyAvatar, Menu, AvatarInputs */
|
MyAvatar, Menu, AvatarInputs, Vec3 */
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
var tabletRezzed = false;
|
var tabletRezzed = false;
|
||||||
|
@ -25,9 +25,6 @@
|
||||||
var debugTablet = false;
|
var debugTablet = false;
|
||||||
var tabletScalePercentage = 100.0;
|
var tabletScalePercentage = 100.0;
|
||||||
UIWebTablet = null;
|
UIWebTablet = null;
|
||||||
var MSECS_PER_SEC = 1000.0;
|
|
||||||
var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone";
|
|
||||||
var gTablet = null;
|
|
||||||
|
|
||||||
Script.include("../libraries/WebTablet.js");
|
Script.include("../libraries/WebTablet.js");
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
class Camera {
|
class SimpleCamera {
|
||||||
protected:
|
protected:
|
||||||
float fov { 60.0f };
|
float fov { 60.0f };
|
||||||
float znear { DEFAULT_NEAR_CLIP }, zfar { DEFAULT_FAR_CLIP };
|
float znear { DEFAULT_NEAR_CLIP }, zfar { DEFAULT_FAR_CLIP };
|
||||||
|
@ -42,7 +42,7 @@ public:
|
||||||
|
|
||||||
std::bitset<KEYS_SIZE> keys;
|
std::bitset<KEYS_SIZE> keys;
|
||||||
|
|
||||||
Camera() {
|
SimpleCamera() {
|
||||||
matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
|
matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class QWindowCamera : public Camera {
|
class QWindowCamera : public SimpleCamera {
|
||||||
Key forKey(int key) {
|
Key forKey(int key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case Qt::Key_W: return FORWARD;
|
case Qt::Key_W: return FORWARD;
|
||||||
|
@ -1067,7 +1067,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void cycleMode() {
|
void cycleMode() {
|
||||||
static auto defaultProjection = Camera().matrices.perspective;
|
static auto defaultProjection = SimpleCamera().matrices.perspective;
|
||||||
_renderMode = (RenderMode)((_renderMode + 1) % RENDER_MODE_COUNT);
|
_renderMode = (RenderMode)((_renderMode + 1) % RENDER_MODE_COUNT);
|
||||||
if (_renderMode == HMD) {
|
if (_renderMode == HMD) {
|
||||||
_camera.matrices.perspective[0] = vec4 { 0.759056330, 0.000000000, 0.000000000, 0.000000000 };
|
_camera.matrices.perspective[0] = vec4 { 0.759056330, 0.000000000, 0.000000000, 0.000000000 };
|
||||||
|
|
Loading…
Reference in a new issue