mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' into address_dialog_focus_fix
This commit is contained in:
commit
75ce8e56df
54 changed files with 997 additions and 524 deletions
|
@ -624,8 +624,8 @@ AudioMixerClientData::IgnoreZone& AudioMixerClientData::IgnoreZoneMemo::get(unsi
|
|||
scale = MIN_IGNORE_BOX_SCALE;
|
||||
}
|
||||
|
||||
// quadruple the scale (this is arbitrary number chosen for comfort)
|
||||
const float IGNORE_BOX_SCALE_FACTOR = 4.0f;
|
||||
// (this is arbitrary number determined empirically for comfort)
|
||||
const float IGNORE_BOX_SCALE_FACTOR = 2.4f;
|
||||
scale *= IGNORE_BOX_SCALE_FACTOR;
|
||||
|
||||
// create the box (we use a box for the zone for convenience)
|
||||
|
|
|
@ -271,8 +271,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) {
|
||||
otherNodeBox.setScaleStayCentered(minBubbleSize);
|
||||
}
|
||||
// Quadruple the scale of both bounding boxes
|
||||
otherNodeBox.embiggen(4.0f);
|
||||
// Change the scale of both bounding boxes
|
||||
// (This is an arbitrary number determined empirically)
|
||||
otherNodeBox.embiggen(2.4f);
|
||||
|
||||
// Perform the collision check between the two bounding boxes
|
||||
if (nodeBox.touches(otherNodeBox)) {
|
||||
|
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68.zip
|
||||
URL_MD5 a068f74d4045e257cfa7926fe6e38ad5
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68-v2.zip
|
||||
URL_MD5 f7d290471baf7f5694c147217b8fc548
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -11,6 +11,35 @@
|
|||
|
||||
include(BundleUtilities)
|
||||
|
||||
# replace copy_resolved_item_into_bundle
|
||||
#
|
||||
# The official version of copy_resolved_item_into_bundle will print out a "warning:" when
|
||||
# the resolved item matches the resolved embedded item. This not not really an issue that
|
||||
# should rise to the level of a "warning" so we replace this message with a "status:"
|
||||
#
|
||||
# Source: https://github.com/jherico/OculusMinimalExample/blob/master/cmake/templates/FixupBundlePostBuild.cmake.in
|
||||
#
|
||||
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
|
||||
if (WIN32)
|
||||
# ignore case on Windows
|
||||
string(TOLOWER "${resolved_item}" resolved_item_compare)
|
||||
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
|
||||
else()
|
||||
set(resolved_item_compare "${resolved_item}")
|
||||
set(resolved_embedded_item_compare "${resolved_embedded_item}")
|
||||
endif()
|
||||
|
||||
if ("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
|
||||
# this is our only change from the original version
|
||||
message(STATUS "status: resolved_item == resolved_embedded_item - not copying...")
|
||||
else()
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
|
||||
if (UNIX AND NOT APPLE)
|
||||
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(gp_resolved_file_type_override resolved_file type_var)
|
||||
if( file MATCHES ".*VCRUNTIME140.*" )
|
||||
set(type "system" PARENT_SCOPE)
|
||||
|
|
|
@ -34,8 +34,9 @@ static const chrono::minutes MAX_REFRESH_TIME { 5 };
|
|||
Q_DECLARE_LOGGING_CATEGORY(asset_backup)
|
||||
Q_LOGGING_CATEGORY(asset_backup, "hifi.asset-backup");
|
||||
|
||||
AssetsBackupHandler::AssetsBackupHandler(const QString& backupDirectory) :
|
||||
_assetsDirectory(backupDirectory + ASSETS_DIR)
|
||||
AssetsBackupHandler::AssetsBackupHandler(const QString& backupDirectory, bool assetServerEnabled) :
|
||||
_assetsDirectory(backupDirectory + ASSETS_DIR),
|
||||
_assetServerEnabled(assetServerEnabled)
|
||||
{
|
||||
// Make sure the asset directory exists.
|
||||
QDir(_assetsDirectory).mkpath(".");
|
||||
|
@ -53,6 +54,7 @@ void AssetsBackupHandler::setupRefreshTimer() {
|
|||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
QObject::connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, [this](SharedNodePointer node) {
|
||||
if (node->getType() == NodeType::AssetServer) {
|
||||
assert(_assetServerEnabled);
|
||||
// run immediately for the first time.
|
||||
_mappingsRefreshTimer.start(0);
|
||||
}
|
||||
|
@ -233,12 +235,12 @@ void AssetsBackupHandler::createBackup(const QString& backupName, QuaZip& zip) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (_lastMappingsRefresh.time_since_epoch().count() == 0) {
|
||||
if (_assetServerEnabled && _lastMappingsRefresh.time_since_epoch().count() == 0) {
|
||||
qCWarning(asset_backup) << "Current mappings not yet loaded.";
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p_high_resolution_clock::now() - _lastMappingsRefresh) > MAX_REFRESH_TIME) {
|
||||
if (_assetServerEnabled && (p_high_resolution_clock::now() - _lastMappingsRefresh) > MAX_REFRESH_TIME) {
|
||||
qCWarning(asset_backup) << "Backing up asset mappings that might be stale.";
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class AssetsBackupHandler : public QObject, public BackupHandlerInterface {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AssetsBackupHandler(const QString& backupDirectory);
|
||||
AssetsBackupHandler(const QString& backupDirectory, bool assetServerEnabled);
|
||||
|
||||
std::pair<bool, float> isAvailable(const QString& backupName) override;
|
||||
std::pair<bool, float> getRecoveryStatus() override;
|
||||
|
@ -65,6 +65,7 @@ private:
|
|||
void updateMappings();
|
||||
|
||||
QString _assetsDirectory;
|
||||
bool _assetServerEnabled { false };
|
||||
|
||||
QTimer _mappingsRefreshTimer;
|
||||
p_high_resolution_clock::time_point _lastMappingsRefresh;
|
||||
|
|
|
@ -307,7 +307,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
connect(_contentManager.get(), &DomainContentBackupManager::started, _contentManager.get(), [this](){
|
||||
_contentManager->addBackupHandler(BackupHandlerPointer(new EntitiesBackupHandler(getEntitiesFilePath(), getEntitiesReplacementFilePath())));
|
||||
_contentManager->addBackupHandler(BackupHandlerPointer(new AssetsBackupHandler(getContentBackupDir())));
|
||||
_contentManager->addBackupHandler(BackupHandlerPointer(new AssetsBackupHandler(getContentBackupDir(), isAssetServerEnabled())));
|
||||
_contentManager->addBackupHandler(BackupHandlerPointer(new ContentSettingsBackupHandler(_settingsManager)));
|
||||
});
|
||||
|
||||
|
@ -991,15 +991,11 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
||||
if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) {
|
||||
|
||||
if (defaultedType == Assignment::AssetServerType) {
|
||||
// Make sure the asset-server is enabled before adding it here.
|
||||
// Initially we do not assign it by default so we can test it in HF domains first
|
||||
static const QString ASSET_SERVER_ENABLED_KEYPATH = "asset_server.enabled";
|
||||
|
||||
if (!_settingsManager.valueOrDefaultValueForKeyPath(ASSET_SERVER_ENABLED_KEYPATH).toBool()) {
|
||||
// skip to the next iteration if asset-server isn't enabled
|
||||
continue;
|
||||
}
|
||||
// Make sure the asset-server is enabled before adding it here.
|
||||
// Initially we do not assign it by default so we can test it in HF domains first
|
||||
if (defaultedType == Assignment::AssetServerType && !isAssetServerEnabled()) {
|
||||
// skip to the next iteraion if asset-server isn't enabled
|
||||
continue;
|
||||
}
|
||||
|
||||
// type has not been set from a command line or config file config, use the default
|
||||
|
@ -2946,6 +2942,12 @@ bool DomainServer::shouldReplicateNode(const Node& node) {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
bool DomainServer::isAssetServerEnabled() {
|
||||
static const QString ASSET_SERVER_ENABLED_KEYPATH = "asset_server.enabled";
|
||||
return _settingsManager.valueOrDefaultValueForKeyPath(ASSET_SERVER_ENABLED_KEYPATH).toBool();
|
||||
}
|
||||
|
||||
void DomainServer::nodeAdded(SharedNodePointer node) {
|
||||
// we don't use updateNodeWithData, so add the DomainServerNodeData to the node here
|
||||
node->setLinkedData(std::unique_ptr<DomainServerNodeData> { new DomainServerNodeData() });
|
||||
|
|
|
@ -72,6 +72,8 @@ public:
|
|||
|
||||
static const QString REPLACEMENT_FILE_EXTENSION;
|
||||
|
||||
bool isAssetServerEnabled();
|
||||
|
||||
public slots:
|
||||
/// Called by NodeList to inform us a node has been added
|
||||
void nodeAdded(SharedNodePointer node);
|
||||
|
|
|
@ -86,7 +86,9 @@ Column {
|
|||
tags: tags,
|
||||
description: description,
|
||||
online_users: data.details.connections || data.details.concurrency || 0,
|
||||
drillDownToPlace: false
|
||||
// Server currently doesn't give isStacked (undefined). Could give bool.
|
||||
drillDownToPlace: (data.isStacked === undefined) ? (data.action !== 'concurrency') : data.isStacked,
|
||||
isStacked: !!data.isStacked
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -124,6 +126,7 @@ Column {
|
|||
onlineUsers: model.online_users;
|
||||
storyId: model.metaverseId;
|
||||
drillDownToPlace: model.drillDownToPlace;
|
||||
isStacked: model.isStacked;
|
||||
|
||||
textPadding: root.textPadding;
|
||||
smallMargin: root.smallMargin;
|
||||
|
|
|
@ -65,6 +65,18 @@ Item {
|
|||
return false;
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
if (openMessage != null) {
|
||||
openMessage.destroy();
|
||||
openMessage = null;
|
||||
}
|
||||
|
||||
if (openModal != null) {
|
||||
openModal.destroy();
|
||||
openModal = null;
|
||||
}
|
||||
}
|
||||
|
||||
function isUrlLoaded(url) {
|
||||
if (currentApp >= 0) {
|
||||
var currentAppUrl = tabletApps.get(currentApp).appUrl;
|
||||
|
|
|
@ -1439,17 +1439,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// add firstRun flag from settings to launch event
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
|
||||
// once the settings have been loaded, check if we need to flip the default for UserActivityLogger
|
||||
auto& userActivityLogger = UserActivityLogger::getInstance();
|
||||
if (!userActivityLogger.isDisabledSettingSet()) {
|
||||
// the user activity logger is opt-out for Interface
|
||||
// but it's defaulted to disabled for other targets
|
||||
// so we need to enable it here if it has never been disabled by the user
|
||||
userActivityLogger.disable(false);
|
||||
}
|
||||
|
||||
QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
|
||||
auto& userActivityLogger = UserActivityLogger::getInstance();
|
||||
if (userActivityLogger.isEnabled()) {
|
||||
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||
|
@ -2607,10 +2599,55 @@ void Application::initializeGL() {
|
|||
_isGLInitialized = true;
|
||||
}
|
||||
|
||||
_glWidget->makeCurrent();
|
||||
glClearColor(0.2f, 0.2f, 0.2f, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glWidget->swapBuffers();
|
||||
if (!_glWidget->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make window context current");
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
// Build a shared canvas / context for the Chromium processes
|
||||
{
|
||||
// Disable signed distance field font rendering on ATI/AMD GPUs, due to
|
||||
// https://highfidelity.manuscript.com/f/cases/13677/Text-showing-up-white-on-Marketplace-app
|
||||
std::string vendor{ (const char*)glGetString(GL_VENDOR) };
|
||||
if ((vendor.find("AMD") != std::string::npos) || (vendor.find("ATI") != std::string::npos)) {
|
||||
qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--disable-distance-field-text"));
|
||||
}
|
||||
|
||||
// Chromium rendering uses some GL functions that prevent nSight from capturing
|
||||
// frames, so we only create the shared context if nsight is NOT active.
|
||||
if (!nsightActive()) {
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->setObjectName("ChromiumShareContext");
|
||||
_chromiumShareContext->create(_glWidget->qglContext());
|
||||
if (!_chromiumShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make chromium shared context current");
|
||||
}
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
_chromiumShareContext->doneCurrent();
|
||||
// Restore the GL widget context
|
||||
if (!_glWidget->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make window context current");
|
||||
}
|
||||
} else {
|
||||
qCWarning(interfaceapp) << "nSight detected, disabling chrome rendering";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Build a shared canvas / context for the QML rendering
|
||||
{
|
||||
_qmlShareContext = new OffscreenGLCanvas();
|
||||
_qmlShareContext->setObjectName("QmlShareContext");
|
||||
_qmlShareContext->create(_glWidget->qglContext());
|
||||
if (!_qmlShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make QML shared context current");
|
||||
}
|
||||
OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext());
|
||||
_qmlShareContext->doneCurrent();
|
||||
if (!_glWidget->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make window context current");
|
||||
}
|
||||
}
|
||||
|
||||
// Build an offscreen GL context for the main thread.
|
||||
_offscreenContext = new OffscreenGLCanvas();
|
||||
|
@ -2622,6 +2659,11 @@ void Application::initializeGL() {
|
|||
_offscreenContext->doneCurrent();
|
||||
_offscreenContext->setThreadContext();
|
||||
|
||||
_glWidget->makeCurrent();
|
||||
glClearColor(0.2f, 0.2f, 0.2f, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glWidget->swapBuffers();
|
||||
|
||||
// Move the GL widget context to the render event handler thread
|
||||
_renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
|
||||
if (!_offscreenContext->makeCurrent()) {
|
||||
|
@ -2744,40 +2786,6 @@ extern void setupPreferences();
|
|||
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false);
|
||||
|
||||
void Application::initializeUi() {
|
||||
// Build a shared canvas / context for the Chromium processes
|
||||
#if !defined(DISABLE_QML)
|
||||
// Chromium rendering uses some GL functions that prevent nSight from capturing
|
||||
// frames, so we only create the shared context if nsight is NOT active.
|
||||
if (!nsightActive()) {
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->setObjectName("ChromiumShareContext");
|
||||
_chromiumShareContext->create(_offscreenContext->getContext());
|
||||
if (!_chromiumShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make chromium shared context current");
|
||||
}
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
_chromiumShareContext->doneCurrent();
|
||||
// Restore the GL widget context
|
||||
_offscreenContext->makeCurrent();
|
||||
} else {
|
||||
qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Build a shared canvas / context for the QML rendering
|
||||
_qmlShareContext = new OffscreenGLCanvas();
|
||||
_qmlShareContext->setObjectName("QmlShareContext");
|
||||
_qmlShareContext->create(_offscreenContext->getContext());
|
||||
if (!_qmlShareContext->makeCurrent()) {
|
||||
qCWarning(interfaceapp, "Unable to make QML shared context current");
|
||||
}
|
||||
OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext());
|
||||
_qmlShareContext->doneCurrent();
|
||||
// Restore the GL widget context
|
||||
_offscreenContext->makeCurrent();
|
||||
// Make sure all QML surfaces share the main thread GL context
|
||||
OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext());
|
||||
|
||||
AddressBarDialog::registerType();
|
||||
ErrorDialog::registerType();
|
||||
LoginDialog::registerType();
|
||||
|
@ -5430,7 +5438,7 @@ void Application::update(float deltaTime) {
|
|||
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
|
||||
// We keep physics disabled until we've received a full scene and everything near the avatar in that
|
||||
// scene is ready to compute its collision shape.
|
||||
if (nearbyEntitiesAreReadyForPhysics()) {
|
||||
if (nearbyEntitiesAreReadyForPhysics() && getMyAvatar()->isReadyForPhysics()) {
|
||||
_physicsEnabled = true;
|
||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ Menu::Menu() {
|
|||
});
|
||||
|
||||
// Edit > Delete
|
||||
auto deleteAction =addActionToQMenuAndActionHash(editMenu, "Delete", QKeySequence::Delete);
|
||||
auto deleteAction = addActionToQMenuAndActionHash(editMenu, "Delete", QKeySequence::Delete);
|
||||
connect(deleteAction, &QAction::triggered, [] {
|
||||
QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::ControlModifier);
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(), keyEvent);
|
||||
|
|
|
@ -21,17 +21,6 @@ AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisi
|
|||
_type = MOTIONSTATE_TYPE_AVATAR;
|
||||
}
|
||||
|
||||
void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
ObjectMotionState::handleEasyChanges(flags);
|
||||
if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && !_body->isActive()) {
|
||||
_body->activate();
|
||||
}
|
||||
}
|
||||
|
||||
bool AvatarMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
||||
AvatarMotionState::~AvatarMotionState() {
|
||||
assert(_avatar);
|
||||
_avatar = nullptr;
|
||||
|
@ -57,9 +46,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
|
|||
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
ShapeInfo shapeInfo;
|
||||
std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
|
||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents();
|
||||
halfExtents.y = 0.0f;
|
||||
_diameter = 2.0f * glm::length(halfExtents);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
|
@ -74,31 +60,25 @@ void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
worldTrans.setRotation(glmToBullet(getObjectRotation()));
|
||||
if (_body) {
|
||||
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
|
||||
_body->setAngularVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||
// HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform
|
||||
// as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie
|
||||
// the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later.
|
||||
const float SPRING_TIMESCALE = 0.5f;
|
||||
float tau = PHYSICS_ENGINE_FIXED_SUBSTEP / SPRING_TIMESCALE;
|
||||
btVector3 currentPosition = worldTrans.getOrigin();
|
||||
btVector3 offsetToTarget = glmToBullet(getObjectPosition()) - currentPosition;
|
||||
float distance = offsetToTarget.length();
|
||||
if ((1.0f - tau) * distance > _diameter) {
|
||||
// the avatar body is far from its target --> slam position
|
||||
btTransform newTransform;
|
||||
newTransform.setOrigin(currentPosition + offsetToTarget);
|
||||
newTransform.setRotation(glmToBullet(getObjectRotation()));
|
||||
_body->setWorldTransform(newTransform);
|
||||
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
|
||||
} else {
|
||||
// the avatar body is near its target --> slam velocity
|
||||
btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget;
|
||||
_body->setLinearVelocity(velocity);
|
||||
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
|
||||
}
|
||||
btVector3 targetPosition = glmToBullet(getObjectPosition());
|
||||
btTransform newTransform;
|
||||
newTransform.setOrigin((1.0f - tau) * currentPosition + tau * targetPosition);
|
||||
newTransform.setRotation(glmToBullet(getObjectRotation()));
|
||||
_body->setWorldTransform(newTransform);
|
||||
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||
_body->setAngularVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||
}
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
|
@ -165,8 +145,3 @@ void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma
|
|||
mask = Physics::getDefaultCollisionMask(group);
|
||||
}
|
||||
|
||||
// virtual
|
||||
float AvatarMotionState::getMass() const {
|
||||
return std::static_pointer_cast<Avatar>(_avatar)->computeMass();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,6 @@ class AvatarMotionState : public ObjectMotionState {
|
|||
public:
|
||||
AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape);
|
||||
|
||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
||||
|
||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
|
@ -67,8 +64,6 @@ public:
|
|||
|
||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
|
||||
virtual float getMass() const override;
|
||||
|
||||
friend class AvatarManager;
|
||||
friend class Avatar;
|
||||
|
||||
|
@ -81,7 +76,6 @@ protected:
|
|||
virtual const btCollisionShape* computeNewShape() override;
|
||||
|
||||
AvatarSharedPointer _avatar;
|
||||
float _diameter { 0.0f };
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
};
|
||||
|
|
|
@ -2430,11 +2430,16 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings
|
|||
if (_domainMinimumHeight > _domainMaximumHeight) {
|
||||
std::swap(_domainMinimumHeight, _domainMaximumHeight);
|
||||
}
|
||||
|
||||
// Set avatar current scale
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
|
||||
// clamp the desired _targetScale by the domain limits NOW, don't try to gracefully animate. Because
|
||||
// this might cause our avatar to become embedded in the terrain.
|
||||
_targetScale = getDomainLimitedScale();
|
||||
|
||||
qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumHeight
|
||||
<< " and a maximum avatar scale of " << _domainMaximumHeight;
|
||||
|
||||
|
@ -2443,6 +2448,8 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings
|
|||
setModelScale(_targetScale);
|
||||
rebuildCollisionShape();
|
||||
settings.endGroup();
|
||||
|
||||
_haveReceivedHeightLimitsFromDomain = true;
|
||||
}
|
||||
|
||||
void MyAvatar::leaveDomain() {
|
||||
|
@ -2460,6 +2467,7 @@ void MyAvatar::saveAvatarScale() {
|
|||
void MyAvatar::clearScaleRestriction() {
|
||||
_domainMinimumHeight = MIN_AVATAR_HEIGHT;
|
||||
_domainMaximumHeight = MAX_AVATAR_HEIGHT;
|
||||
_haveReceivedHeightLimitsFromDomain = false;
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocation(const QVariant& propertiesVar) {
|
||||
|
@ -2577,8 +2585,12 @@ bool MyAvatar::safeLanding(const glm::vec3& position) {
|
|||
|
||||
// If position is not reliably safe from being stuck by physics, answer true and place a candidate better position in betterPositionOut.
|
||||
bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& betterPositionOut) {
|
||||
|
||||
// We begin with utilities and tests. The Algorithm in four parts is below.
|
||||
auto halfHeight = _characterController.getCapsuleHalfHeight() + _characterController.getCapsuleRadius();
|
||||
// NOTE: we use estimated avatar height here instead of the bullet capsule halfHeight, because
|
||||
// the domain avatar height limiting might not have taken effect yet on the actual bullet shape.
|
||||
auto halfHeight = 0.5f * getHeight();
|
||||
|
||||
if (halfHeight == 0) {
|
||||
return false; // zero height avatar
|
||||
}
|
||||
|
@ -2587,14 +2599,13 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette
|
|||
return false; // no entity tree
|
||||
}
|
||||
// More utilities.
|
||||
const auto offset = getWorldOrientation() *_characterController.getCapsuleLocalOffset();
|
||||
const auto capsuleCenter = positionIn + offset;
|
||||
const auto capsuleCenter = positionIn;
|
||||
const auto up = _worldUpDirection, down = -up;
|
||||
glm::vec3 upperIntersection, upperNormal, lowerIntersection, lowerNormal;
|
||||
EntityItemID upperId, lowerId;
|
||||
QVector<EntityItemID> include{}, ignore{};
|
||||
auto mustMove = [&] { // Place bottom of capsule at the upperIntersection, and check again based on the capsule center.
|
||||
betterPositionOut = upperIntersection + (up * halfHeight) - offset;
|
||||
betterPositionOut = upperIntersection + (up * halfHeight);
|
||||
return true;
|
||||
};
|
||||
auto findIntersection = [&](const glm::vec3& startPointIn, const glm::vec3& directionIn, glm::vec3& intersectionOut, EntityItemID& entityIdOut, glm::vec3& normalOut) {
|
||||
|
@ -2614,7 +2625,7 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette
|
|||
EntityItemID entityID = entityTree->findRayIntersection(startPointIn, directionIn, include, ignore, visibleOnly, collidableOnly, precisionPicking,
|
||||
element, distance, face, normalOut, extraInfo, lockType, accurateResult);
|
||||
if (entityID.isNull()) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
intersectionOut = startPointIn + (directionIn * distance);
|
||||
entityIdOut = entityID;
|
||||
|
@ -2640,7 +2651,7 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette
|
|||
// I.e., we are in a clearing between two objects.
|
||||
if (isDown(upperNormal) && isUp(lowerNormal)) {
|
||||
auto spaceBetween = glm::distance(upperIntersection, lowerIntersection);
|
||||
const float halfHeightFactor = 2.5f; // Until case 5003 is fixed (and maybe after?), we need a fudge factor. Also account for content modelers not being precise.
|
||||
const float halfHeightFactor = 2.25f; // Until case 5003 is fixed (and maybe after?), we need a fudge factor. Also account for content modelers not being precise.
|
||||
if (spaceBetween > (halfHeightFactor * halfHeight)) {
|
||||
// There is room for us to fit in that clearing. If there wasn't, physics would oscilate us between the objects above and below.
|
||||
// We're now going to iterate upwards through successive upperIntersections, testing to see if we're contained within the top surface of some entity.
|
||||
|
@ -3090,6 +3101,10 @@ float MyAvatar::getWalkSpeed() const {
|
|||
return _walkSpeed.get() * _walkSpeedScalar;
|
||||
}
|
||||
|
||||
bool MyAvatar::isReadyForPhysics() const {
|
||||
return qApp->isServerlessMode() || _haveReceivedHeightLimitsFromDomain;
|
||||
}
|
||||
|
||||
void MyAvatar::setSprintMode(bool sprint) {
|
||||
_walkSpeedScalar = sprint ? AVATAR_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
|
||||
}
|
||||
|
|
|
@ -1015,6 +1015,8 @@ public:
|
|||
|
||||
QVector<QString> getScriptUrls();
|
||||
|
||||
bool isReadyForPhysics() const;
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1610,6 +1612,8 @@ private:
|
|||
|
||||
// load avatar scripts once when rig is ready
|
||||
bool _shouldLoadScripts { false };
|
||||
|
||||
bool _haveReceivedHeightLimitsFromDomain = { false };
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -227,10 +227,13 @@ QString QmlCommerce::getInstalledApps() {
|
|||
QString scriptURL = appFileJsonObject["scriptURL"].toString();
|
||||
|
||||
// If the script .app.json is on the user's local disk but the associated script isn't running
|
||||
// for some reason, start that script again.
|
||||
// for some reason (i.e. the user stopped it from Running Scripts),
|
||||
// delete the .app.json from the user's local disk.
|
||||
if (!runningScripts.contains(scriptURL)) {
|
||||
if ((DependencyManager::get<ScriptEngines>()->loadScript(scriptURL.trimmed())).isNull()) {
|
||||
qCDebug(commerce) << "Couldn't start script while checking installed apps.";
|
||||
if (!appFile.remove()) {
|
||||
qCWarning(commerce)
|
||||
<< "Couldn't delete local .app.json file (app's script isn't running). App filename is:"
|
||||
<< appFileName;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -81,6 +81,13 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
// Instance UserActivityLogger now that the settings are loaded
|
||||
auto& ual = UserActivityLogger::getInstance();
|
||||
// once the settings have been loaded, check if we need to flip the default for UserActivityLogger
|
||||
if (!ual.isDisabledSettingSet()) {
|
||||
// the user activity logger is opt-out for Interface
|
||||
// but it's defaulted to disabled for other targets
|
||||
// so we need to enable it here if it has never been disabled by the user
|
||||
ual.disable(false);
|
||||
}
|
||||
qDebug() << "UserActivityLogger is enabled:" << ual.isEnabled();
|
||||
|
||||
if (ual.isEnabled()) {
|
||||
|
|
|
@ -93,10 +93,18 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) {
|
|||
static const QUrl url("dialogs/TabletConnectionFailureDialog.qml");
|
||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||
if (visible) {
|
||||
_dialogCreatedWhileShown = tablet->property("tabletShown").toBool();
|
||||
tablet->initialScreen(url);
|
||||
if (!hmd->getShouldShowTablet()) {
|
||||
hmd->openTablet();
|
||||
}
|
||||
} else if (tablet->isPathLoaded(url)) {
|
||||
tablet->closeDialog();
|
||||
tablet->gotoHomeScreen();
|
||||
if (!_dialogCreatedWhileShown) {
|
||||
hmd->closeTablet();
|
||||
}
|
||||
_dialogCreatedWhileShown = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ private:
|
|||
QPointer<OctreeStatsDialog> _octreeStatsDialog;
|
||||
QPointer<TestingDialog> _testingDialog;
|
||||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
bool _dialogCreatedWhileShown { false };
|
||||
bool _addressBarVisible { false };
|
||||
};
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "InterfaceLogging.h"
|
||||
|
||||
OverlayConductor::OverlayConductor() {
|
||||
|
||||
}
|
||||
|
||||
OverlayConductor::~OverlayConductor() {
|
||||
|
@ -33,8 +32,8 @@ bool OverlayConductor::headOutsideOverlay() const {
|
|||
glm::vec3 uiPos = uiTransform.getTranslation();
|
||||
glm::vec3 uiForward = uiTransform.getRotation() * glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
|
||||
const float MAX_COMPOSITOR_DISTANCE = 0.99f; // If you're 1m from center of ui sphere, you're at the surface.
|
||||
const float MAX_COMPOSITOR_ANGLE = 180.0f; // rotation check is effectively disabled
|
||||
const float MAX_COMPOSITOR_DISTANCE = 0.99f; // If you're 1m from center of ui sphere, you're at the surface.
|
||||
const float MAX_COMPOSITOR_ANGLE = 180.0f; // rotation check is effectively disabled
|
||||
if (glm::distance(uiPos, hmdPos) > MAX_COMPOSITOR_DISTANCE ||
|
||||
glm::dot(uiForward, hmdForward) < cosf(glm::radians(MAX_COMPOSITOR_ANGLE))) {
|
||||
return true;
|
||||
|
@ -43,10 +42,9 @@ bool OverlayConductor::headOutsideOverlay() const {
|
|||
}
|
||||
|
||||
bool OverlayConductor::updateAvatarIsAtRest() {
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
const quint64 REST_ENABLE_TIME_USECS = 1000 * 1000; // 1 s
|
||||
const quint64 REST_ENABLE_TIME_USECS = 1000 * 1000; // 1 s
|
||||
const quint64 REST_DISABLE_TIME_USECS = 200 * 1000; // 200 ms
|
||||
|
||||
const float AT_REST_THRESHOLD = 0.01f;
|
||||
|
@ -69,31 +67,6 @@ bool OverlayConductor::updateAvatarIsAtRest() {
|
|||
return _currentAtRest;
|
||||
}
|
||||
|
||||
bool OverlayConductor::updateAvatarHasDriveInput() {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
const quint64 DRIVE_ENABLE_TIME_USECS = 200 * 1000; // 200 ms
|
||||
const quint64 DRIVE_DISABLE_TIME_USECS = 1000 * 1000; // 1 s
|
||||
|
||||
bool desiredDriving = myAvatar->hasDriveInput();
|
||||
if (desiredDriving != _desiredDriving) {
|
||||
// start timer
|
||||
_desiredDrivingTimer = usecTimestampNow() + (desiredDriving ? DRIVE_ENABLE_TIME_USECS : DRIVE_DISABLE_TIME_USECS);
|
||||
}
|
||||
|
||||
_desiredDriving = desiredDriving;
|
||||
|
||||
if (_desiredDrivingTimer != 0 && usecTimestampNow() > _desiredDrivingTimer) {
|
||||
// timer expired
|
||||
// change state!
|
||||
_currentDriving = _desiredDriving;
|
||||
// disable timer
|
||||
_desiredDrivingTimer = 0;
|
||||
}
|
||||
|
||||
return _currentDriving;
|
||||
}
|
||||
|
||||
void OverlayConductor::centerUI() {
|
||||
// place the overlay at the current hmd position in sensor space
|
||||
auto camMat = cancelOutRollAndPitch(qApp->getHMDSensorPose());
|
||||
|
@ -115,20 +88,19 @@ void OverlayConductor::update(float dt) {
|
|||
_hmdMode = false;
|
||||
}
|
||||
|
||||
bool prevDriving = _currentDriving;
|
||||
bool isDriving = updateAvatarHasDriveInput();
|
||||
bool drivingChanged = prevDriving != isDriving;
|
||||
bool isAtRest = updateAvatarIsAtRest();
|
||||
bool isMoving = !isAtRest;
|
||||
|
||||
bool shouldRecenter = false;
|
||||
|
||||
if (_flags & SuppressedByDrive) {
|
||||
if (!isDriving) {
|
||||
_flags &= ~SuppressedByDrive;
|
||||
shouldRecenter = true;
|
||||
if (_flags & SuppressedByMove) {
|
||||
if (!isMoving) {
|
||||
_flags &= ~SuppressedByMove;
|
||||
shouldRecenter = true;
|
||||
}
|
||||
} else {
|
||||
if (myAvatar->getClearOverlayWhenMoving() && drivingChanged && isDriving) {
|
||||
_flags |= SuppressedByDrive;
|
||||
if (myAvatar->getClearOverlayWhenMoving() && isMoving) {
|
||||
_flags |= SuppressedByMove;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +115,6 @@ void OverlayConductor::update(float dt) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
bool targetVisible = Menu::getInstance()->isOptionChecked(MenuOption::Overlays) && (0 == (_flags & SuppressMask));
|
||||
if (targetVisible != currentVisible) {
|
||||
offscreenUi->setPinned(!targetVisible);
|
||||
|
|
|
@ -23,23 +23,17 @@ public:
|
|||
|
||||
private:
|
||||
bool headOutsideOverlay() const;
|
||||
bool updateAvatarHasDriveInput();
|
||||
bool updateAvatarIsAtRest();
|
||||
|
||||
enum SupressionFlags {
|
||||
SuppressedByDrive = 0x01,
|
||||
SuppressedByMove = 0x01,
|
||||
SuppressedByHead = 0x02,
|
||||
SuppressMask = 0x03,
|
||||
};
|
||||
|
||||
uint8_t _flags { SuppressedByDrive };
|
||||
uint8_t _flags { SuppressedByMove };
|
||||
bool _hmdMode { false };
|
||||
|
||||
// used by updateAvatarHasDriveInput
|
||||
uint64_t _desiredDrivingTimer { 0 };
|
||||
bool _desiredDriving { false };
|
||||
bool _currentDriving { false };
|
||||
|
||||
// used by updateAvatarIsAtRest
|
||||
uint64_t _desiredAtRestTimer { 0 };
|
||||
bool _desiredAtRest { true };
|
||||
|
|
|
@ -35,6 +35,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
|
|||
QString thumbnailUrl = dataObject.value("thumbnail_url").toString();
|
||||
QString imageUrl = dataObject.value("image_url").toString();
|
||||
QString snapshotID = dataObject.value("id").toString();
|
||||
QString originalImageFileName = dataObject.value("original_image_file_name").toString();
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
QString placeName = _inWorldLocation.authority(); // We currently only upload shareable places, in which case this is just host.
|
||||
QString currentPath = _inWorldLocation.path();
|
||||
|
@ -48,6 +49,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
|
|||
detailsObject.insert("shareable_url", dataObject.value("shareable_url").toString());
|
||||
}
|
||||
detailsObject.insert("snapshot_id", snapshotID);
|
||||
detailsObject.insert("original_image_file_name", originalImageFileName);
|
||||
QString pickledDetails = QJsonDocument(detailsObject).toJson();
|
||||
userStoryObject.insert("details", pickledDetails);
|
||||
userStoryObject.insert("thumbnail_url", thumbnailUrl);
|
||||
|
|
|
@ -131,6 +131,9 @@ void ModelOverlay::update(float deltatime) {
|
|||
|
||||
if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) {
|
||||
_texturesLoaded = true;
|
||||
if (!_modelTextures.isEmpty()) {
|
||||
_model->setTextures(_modelTextures);
|
||||
}
|
||||
_model->updateRenderItems();
|
||||
}
|
||||
}
|
||||
|
@ -229,8 +232,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
if (texturesValue.isValid() && texturesValue.canConvert(QVariant::Map)) {
|
||||
_texturesLoaded = false;
|
||||
QVariantMap textureMap = texturesValue.toMap();
|
||||
QMetaObject::invokeMethod(_model.get(), "setTextures", Qt::AutoConnection,
|
||||
Q_ARG(const QVariantMap&, textureMap));
|
||||
_modelTextures = textureMap;
|
||||
}
|
||||
|
||||
auto groupCulledValue = properties["isGroupCulled"];
|
||||
|
|
|
@ -861,6 +861,7 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size());
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
|
@ -1543,14 +1544,12 @@ void Avatar::updateDisplayNameAlpha(bool showDisplayName) {
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||
float uniformScale = getModelScale();
|
||||
float radius = uniformScale * _skeletonModel->getBoundingCapsuleRadius();
|
||||
float height = uniformScale * _skeletonModel->getBoundingCapsuleHeight();
|
||||
shapeInfo.setCapsuleY(radius, 0.5f * height);
|
||||
|
||||
glm::vec3 offset = uniformScale * _skeletonModel->getBoundingCapsuleOffset();
|
||||
shapeInfo.setOffset(offset);
|
||||
shapeInfo.setCapsuleY(uniformScale * _skeletonModel->getBoundingCapsuleRadius(),
|
||||
0.5f * uniformScale * _skeletonModel->getBoundingCapsuleHeight());
|
||||
shapeInfo.setOffset(uniformScale * _skeletonModel->getBoundingCapsuleOffset());
|
||||
}
|
||||
|
||||
void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
||||
|
@ -1573,8 +1572,9 @@ float Avatar::computeMass() {
|
|||
return _density * TWO_PI * radius * radius * (glm::length(end - start) + 2.0f * radius / 3.0f);
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::rebuildCollisionShape() {
|
||||
addPhysicsFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
addPhysicsFlags(Simulation::DIRTY_SHAPE);
|
||||
}
|
||||
|
||||
void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) {
|
||||
|
|
|
@ -157,18 +157,19 @@ void TextureBaker::processTexture() {
|
|||
return;
|
||||
}
|
||||
|
||||
const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
|
||||
if (name == nullptr) {
|
||||
handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// attempt to write the baked texture to the destination file path
|
||||
{
|
||||
if (memKTX->_header.isCompressed()) {
|
||||
const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
|
||||
if (name == nullptr) {
|
||||
handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
|
||||
const size_t length = memKTX->_storage->size();
|
||||
|
||||
auto fileName = _baseFilename + BAKED_TEXTURE_BCN_SUFFIX;
|
||||
auto fileName = _baseFilename + "_" + name + ".ktx";
|
||||
auto filePath = _outputDirectory.absoluteFilePath(fileName);
|
||||
QFile bakedTextureFile { filePath };
|
||||
if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
|
||||
|
@ -177,6 +178,18 @@ void TextureBaker::processTexture() {
|
|||
}
|
||||
_outputFiles.push_back(filePath);
|
||||
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
|
||||
} else {
|
||||
const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
|
||||
const size_t length = memKTX->_storage->size();
|
||||
|
||||
auto fileName = _baseFilename + ".ktx";
|
||||
auto filePath = _outputDirectory.absoluteFilePath(fileName);
|
||||
QFile ktxTextureFile { filePath };
|
||||
if (!ktxTextureFile.open(QIODevice::WriteOnly) || ktxTextureFile.write(data, length) == -1) {
|
||||
handleError("Could not write ktx texture for " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
_outputFiles.push_back(filePath);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ layout(std140) uniform particleBuffer {
|
|||
ParticleUniforms particle;
|
||||
};
|
||||
|
||||
in vec3 inPosition;
|
||||
in vec2 inColor; // This is actual Lifetime + Seed
|
||||
layout(location=0) in vec3 inPosition;
|
||||
layout(location=2) in vec2 inColor; // This is actual Lifetime + Seed
|
||||
|
||||
out vec4 varColor;
|
||||
out vec2 varTexcoord;
|
||||
|
|
|
@ -46,7 +46,9 @@ bool DEV_DECIMATE_TEXTURES = false;
|
|||
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
|
||||
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
|
||||
|
||||
static const auto HDR_FORMAT = gpu::Element::COLOR_R11G11B10;
|
||||
// we use a ref here to work around static order initialization
|
||||
// possibly causing the element not to be constructed yet
|
||||
static const auto& HDR_FORMAT = gpu::Element::COLOR_R11G11B10;
|
||||
|
||||
static std::atomic<bool> compressColorTextures { false };
|
||||
static std::atomic<bool> compressNormalTextures { false };
|
||||
|
|
|
@ -157,7 +157,11 @@ void AddressManager::storeCurrentAddress() {
|
|||
// be loaded over http(s)
|
||||
// url.scheme() == URL_SCHEME_HTTP ||
|
||||
// url.scheme() == URL_SCHEME_HTTPS ||
|
||||
currentAddressHandle.set(url);
|
||||
if (isConnected()) {
|
||||
currentAddressHandle.set(url);
|
||||
} else {
|
||||
qCWarning(networking) << "Ignoring attempt to save current address because not connected to domain:" << url;
|
||||
}
|
||||
} else {
|
||||
qCWarning(networking) << "Ignoring attempt to save current address with an invalid url:" << url;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,11 @@ ResourceManager::ResourceManager(bool atpSupportEnabled) : _atpSupportEnabled(at
|
|||
_thread.start();
|
||||
}
|
||||
|
||||
ResourceManager::~ResourceManager() {
|
||||
_thread.terminate();
|
||||
_thread.wait();
|
||||
}
|
||||
|
||||
void ResourceManager::setUrlPrefixOverride(const QString& prefix, const QString& replacement) {
|
||||
QMutexLocker locker(&_prefixMapLock);
|
||||
if (replacement.isEmpty()) {
|
||||
|
|
|
@ -28,6 +28,7 @@ class ResourceManager: public QObject, public Dependency {
|
|||
|
||||
public:
|
||||
ResourceManager(bool atpSupportEnabled = true);
|
||||
~ResourceManager();
|
||||
|
||||
void setUrlPrefixOverride(const QString& prefix, const QString& replacement);
|
||||
QString normalizeURL(const QString& urlString);
|
||||
|
|
|
@ -111,7 +111,7 @@ public:
|
|||
virtual PhysicsMotionType getMotionType() const { return _motionType; }
|
||||
|
||||
void setMass(float mass);
|
||||
virtual float getMass() const;
|
||||
float getMass() const;
|
||||
|
||||
void setBodyLinearVelocity(const glm::vec3& velocity) const;
|
||||
void setBodyAngularVelocity(const glm::vec3& velocity) const;
|
||||
|
|
|
@ -105,6 +105,10 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
}
|
||||
case MOTION_TYPE_DYNAMIC: {
|
||||
mass = motionState->getMass();
|
||||
if (mass != mass || mass < 1.0f) {
|
||||
qCDebug(physics) << "mass is too low, setting to 1.0 Kg --" << mass;
|
||||
mass = 1.0f;
|
||||
}
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(motionState->getShape());
|
||||
assert(shape);
|
||||
shape->calculateLocalInertia(mass, inertia);
|
||||
|
|
|
@ -1074,15 +1074,11 @@ int Model::getLastFreeJointIndex(int jointIndex) const {
|
|||
|
||||
void Model::setTextures(const QVariantMap& textures) {
|
||||
if (isLoaded()) {
|
||||
_needsUpdateTextures = true;
|
||||
_pendingTextures.clear();
|
||||
_needsFixupInScene = true;
|
||||
_renderGeometry->setTextures(textures);
|
||||
} else {
|
||||
// FIXME(Huffman): Disconnect previously connected lambdas so we don't set textures multiple
|
||||
// after the geometry has finished loading.
|
||||
connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, [this, textures]() {
|
||||
_renderGeometry->setTextures(textures);
|
||||
});
|
||||
_pendingTextures = textures;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1106,7 +1102,8 @@ void Model::setURL(const QUrl& url) {
|
|||
}
|
||||
|
||||
_needsReload = true;
|
||||
_needsUpdateTextures = true;
|
||||
// One might be tempted to _pendingTextures.clear(), thinking that a new URL means an old texture doesn't apply.
|
||||
// But sometimes, particularly when first setting the values, the texture might be set first. So let's not clear here.
|
||||
_visualGeometryRequestFailed = false;
|
||||
_needsFixupInScene = true;
|
||||
invalidCalculatedMeshBoxes();
|
||||
|
@ -1123,6 +1120,8 @@ void Model::setURL(const QUrl& url) {
|
|||
void Model::loadURLFinished(bool success) {
|
||||
if (!success) {
|
||||
_visualGeometryRequestFailed = true;
|
||||
} else if (!_pendingTextures.empty()) {
|
||||
setTextures(_pendingTextures);
|
||||
}
|
||||
emit setURLFinished(success);
|
||||
}
|
||||
|
|
|
@ -457,7 +457,7 @@ protected:
|
|||
bool _needsFixupInScene { true }; // needs to be removed/re-added to scene
|
||||
bool _needsReload { true };
|
||||
bool _needsUpdateClusterMatrices { true };
|
||||
mutable bool _needsUpdateTextures { true };
|
||||
QVariantMap _pendingTextures { };
|
||||
|
||||
friend class ModelMeshPartPayload;
|
||||
Rig _rig;
|
||||
|
|
|
@ -431,6 +431,19 @@ bool TabletProxy::isMessageDialogOpen() {
|
|||
return result.toBool();
|
||||
}
|
||||
|
||||
void TabletProxy::closeDialog() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "closeDialog");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_qmlTabletRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "closeDialog");
|
||||
}
|
||||
|
||||
void TabletProxy::emitWebEvent(const QVariant& msg) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitWebEvent", Q_ARG(QVariant, msg));
|
||||
|
|
|
@ -308,6 +308,12 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE bool isMessageDialogOpen();
|
||||
|
||||
/**jsdoc
|
||||
* Close any open dialogs.
|
||||
* @function TabletProxy#closeDialog
|
||||
*/
|
||||
Q_INVOKABLE void closeDialog();
|
||||
|
||||
/**jsdoc
|
||||
* Creates a new button, adds it to this and returns it.
|
||||
* @function TabletProxy#addButton
|
||||
|
|
|
@ -273,7 +273,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
this.shouldSendStart = false;
|
||||
this.equipedWithSecondary = false;
|
||||
this.handHasBeenRightsideUp = false;
|
||||
this.mouseEquip = false;
|
||||
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
300,
|
||||
|
@ -283,11 +282,10 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
|
||||
var equipHotspotBuddy = new EquipHotspotBuddy();
|
||||
|
||||
this.setMessageGrabData = function(entityProperties, mouseEquip) {
|
||||
this.setMessageGrabData = function(entityProperties) {
|
||||
if (entityProperties) {
|
||||
this.messageGrabEntity = true;
|
||||
this.grabEntityProps = entityProperties;
|
||||
this.mouseEquip = mouseEquip;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -584,7 +582,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
this.targetEntityID = null;
|
||||
this.messageGrabEntity = false;
|
||||
this.grabEntityProps = null;
|
||||
this.mouseEquip = false;
|
||||
};
|
||||
|
||||
this.updateInputs = function (controllerData) {
|
||||
|
@ -630,14 +627,12 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
|
||||
// if the potentialHotspot is cloneable, clone it and return it
|
||||
// if the potentialHotspot os not cloneable and locked return null
|
||||
|
||||
if (potentialEquipHotspot &&
|
||||
(((this.triggerSmoothedSqueezed() || this.secondarySmoothedSqueezed()) && !this.waitForTriggerRelease) ||
|
||||
this.messageGrabEntity)) {
|
||||
this.grabbedHotspot = potentialEquipHotspot;
|
||||
this.targetEntityID = this.grabbedHotspot.entityID;
|
||||
this.startEquipEntity(controllerData);
|
||||
this.messageGrabEntity = false;
|
||||
this.equipedWithSecondary = this.secondarySmoothedSqueezed();
|
||||
return makeRunningValues(true, [potentialEquipHotspot.entityID], []);
|
||||
} else {
|
||||
|
@ -661,7 +656,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
var timestamp = Date.now();
|
||||
this.updateInputs(controllerData);
|
||||
|
||||
if (!this.mouseEquip && !this.isTargetIDValid(controllerData)) {
|
||||
if (!this.messageGrabEntity && !this.isTargetIDValid(controllerData)) {
|
||||
this.endEquipEntity();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
@ -762,9 +757,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
var equipModule = (data.hand === "left") ? leftEquipEntity : rightEquipEntity;
|
||||
var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES);
|
||||
entityProperties.id = data.entityID;
|
||||
var mouseEquip = false;
|
||||
equipModule.setMessageGrabData(entityProperties, mouseEquip);
|
||||
|
||||
equipModule.setMessageGrabData(entityProperties);
|
||||
} catch (e) {
|
||||
print("WARNING: equipEntity.js -- error parsing Hifi-Hand-Grab message: " + message);
|
||||
}
|
||||
|
@ -812,15 +805,14 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition);
|
||||
var leftHandAvailable = leftEquipEntity.targetEntityID === null;
|
||||
var rightHandAvailable = rightEquipEntity.targetEntityID === null;
|
||||
var mouseEquip = true;
|
||||
if (rightHandAvailable && (distanceToRightHand < distanceToLeftHand || !leftHandAvailable)) {
|
||||
// clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags)
|
||||
clearGrabActions(entityID);
|
||||
rightEquipEntity.setMessageGrabData(entityProperties, mouseEquip);
|
||||
rightEquipEntity.setMessageGrabData(entityProperties);
|
||||
} else if (leftHandAvailable && (distanceToLeftHand < distanceToRightHand || !rightHandAvailable)) {
|
||||
// clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags)
|
||||
clearGrabActions(entityID);
|
||||
leftEquipEntity.setMessageGrabData(entityProperties, mouseEquip);
|
||||
leftEquipEntity.setMessageGrabData(entityProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
this.previousRoomControllerPosition = roomControllerPosition;
|
||||
};
|
||||
|
||||
this.endNearGrabAction = function () {
|
||||
this.endFarGrabAction = function () {
|
||||
ensureDynamic(this.grabbedThingID);
|
||||
this.distanceHolding = false;
|
||||
this.distanceRotating = false;
|
||||
|
@ -402,7 +402,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
this.run = function (controllerData) {
|
||||
if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE ||
|
||||
this.notPointingAtEntity(controllerData) || this.targetIsNull()) {
|
||||
this.endNearGrabAction();
|
||||
this.endFarGrabAction();
|
||||
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity",
|
||||
this.highlightedEntity);
|
||||
this.highlightedEntity = null;
|
||||
|
@ -430,11 +430,12 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
}
|
||||
|
||||
if (this.actionID) {
|
||||
// if we are doing a distance grab and the object gets close enough to the controller,
|
||||
// if we are doing a distance grab and the object or tablet gets close enough to the controller,
|
||||
// stop the far-grab so the near-grab or equip can take over.
|
||||
for (var k = 0; k < nearGrabReadiness.length; k++) {
|
||||
if (nearGrabReadiness[k].active && nearGrabReadiness[k].targets[0] === this.grabbedThingID) {
|
||||
this.endNearGrabAction();
|
||||
if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.grabbedThingID
|
||||
|| HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) {
|
||||
this.endFarGrabAction();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +446,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
// where it could near-grab something, stop searching.
|
||||
for (var j = 0; j < nearGrabReadiness.length; j++) {
|
||||
if (nearGrabReadiness[j].active) {
|
||||
this.endNearGrabAction();
|
||||
this.endFarGrabAction();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
|
@ -577,7 +578,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
var disableModule = getEnabledModuleByName(moduleName);
|
||||
if (disableModule) {
|
||||
if (disableModule.disableModules) {
|
||||
this.endNearGrabAction();
|
||||
this.endFarGrabAction();
|
||||
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity",
|
||||
this.highlightedEntity);
|
||||
this.highlightedEntity = null;
|
||||
|
|
|
@ -14,6 +14,7 @@ var CONTOLLER_SCRIPTS = [
|
|||
"controllerDisplayManager.js",
|
||||
"grab.js",
|
||||
"toggleAdvancedMovementForHandControllers.js",
|
||||
"handTouch.js",
|
||||
"controllerDispatcher.js",
|
||||
"controllerModules/nearParentGrabEntity.js",
|
||||
"controllerModules/nearParentGrabOverlay.js",
|
||||
|
|
|
@ -14,29 +14,29 @@
|
|||
/* global Script, Overlays, Controller, Vec3, MyAvatar, Entities
|
||||
*/
|
||||
|
||||
(function(){
|
||||
(function() {
|
||||
|
||||
var MSECONDS_AFTER_LOAD = 2000;
|
||||
|
||||
var updateFingerWithIndex = 0;
|
||||
|
||||
|
||||
// Keys to access finger data
|
||||
var fingerKeys = ["pinky", "ring", "middle", "index", "thumb"];
|
||||
|
||||
// Additionally close the hands to achieve a grabbing effect
|
||||
var grabPercent = { left: 0,
|
||||
right: 0 };
|
||||
|
||||
// var isGrabbing = false;
|
||||
var grabPercent = { left: 0, right: 0 };
|
||||
|
||||
var Palm = function() {
|
||||
this.position = {x:0, y:0, z:0};
|
||||
this.perpendicular = {x:0, y:0, z:0};
|
||||
this.position = {x: 0, y: 0, z: 0};
|
||||
this.perpendicular = {x: 0, y: 0, z: 0};
|
||||
this.distance = 0;
|
||||
this.fingers = {
|
||||
pinky: {x:0, y:0, z:0},
|
||||
middle: {x:0, y:0, z:0},
|
||||
ring: {x:0, y:0, z:0},
|
||||
thumb: {x:0, y:0, z:0},
|
||||
index: {x:0, y:0, z:0}
|
||||
pinky: {x: 0, y: 0, z: 0},
|
||||
middle: {x: 0, y: 0, z: 0},
|
||||
ring: {x: 0, y: 0, z: 0},
|
||||
thumb: {x: 0, y: 0, z: 0},
|
||||
index: {x: 0, y: 0, z: 0}
|
||||
};
|
||||
this.set = false;
|
||||
};
|
||||
|
@ -72,52 +72,132 @@
|
|||
right: 0
|
||||
};
|
||||
|
||||
// joint data for opened pose
|
||||
|
||||
// joint data for open pose
|
||||
var dataOpen = {
|
||||
left: {
|
||||
pinky:[{x: -0.0066, y:-0.0224, z:-0.2174, w:0.9758},{x: 0.0112, y:0.0001, z:0.0093, w:0.9999},{x: -0.0346, y:0.0003, z:-0.0073, w:0.9994}],
|
||||
ring:[{x: -0.0029, y:-0.0094, z:-0.1413, w:0.9899},{x: 0.0112, y:0.0001, z:0.0059, w:0.9999},{x: -0.0346, y:0.0002, z:-0.006, w:0.9994}],
|
||||
middle:[{x: -0.0016, y:0, z:-0.0286, w:0.9996},{x: 0.0112, y:-0.0001, z:-0.0063, w:0.9999},{x: -0.0346, y:-0.0003, z:0.0073, w:0.9994}],
|
||||
index:[{x: -0.0016, y:0.0001, z:0.0199, w:0.9998},{x: 0.0112, y:0, z:0.0081, w:0.9999},{x: -0.0346, y:0.0008, z:-0.023, w:0.9991}],
|
||||
thumb:[{x: 0.0354, y:0.0363, z:0.3275, w:0.9435},{x: -0.0945, y:0.0938, z:0.0995, w:0.9861},{x: -0.0952, y:0.0718, z:0.1382, w:0.9832}]
|
||||
pinky: [
|
||||
{x: -0.0066, y: -0.0224, z: -0.2174, w: 0.9758},
|
||||
{x: 0.0112, y: 0.0001, z: 0.0093, w: 0.9999},
|
||||
{x: -0.0346, y: 0.0003, z: -0.0073, w: 0.9994}
|
||||
],
|
||||
ring: [
|
||||
{x: -0.0029, y: -0.0094, z: -0.1413, w: 0.9899},
|
||||
{x: 0.0112, y: 0.0001, z: 0.0059, w: 0.9999},
|
||||
{x: -0.0346, y: 0.0002, z: -0.006, w: 0.9994}
|
||||
],
|
||||
middle: [
|
||||
{x: -0.0016, y: 0, z: -0.0286, w: 0.9996},
|
||||
{x: 0.0112, y: -0.0001, z: -0.0063, w: 0.9999},
|
||||
{x: -0.0346, y: -0.0003, z: 0.0073, w: 0.9994}
|
||||
],
|
||||
index: [
|
||||
{x: -0.0016, y: 0.0001, z: 0.0199, w: 0.9998},
|
||||
{x: 0.0112, y: 0, z: 0.0081, w: 0.9999},
|
||||
{x: -0.0346, y: 0.0008, z: -0.023, w: 0.9991}
|
||||
],
|
||||
thumb: [
|
||||
{x: 0.0354, y: 0.0363, z: 0.3275, w: 0.9435},
|
||||
{x: -0.0945, y: 0.0938, z: 0.0995, w: 0.9861},
|
||||
{x: -0.0952, y: 0.0718, z: 0.1382, w: 0.9832}
|
||||
]
|
||||
}, right: {
|
||||
pinky:[{x: -0.0034, y:0.023, z:0.1051, w:0.9942},{x: 0.0106, y:-0.0001, z:-0.0091, w:0.9999},{x: -0.0346, y:-0.0003, z:0.0075, w:0.9994}],
|
||||
ring:[{x: -0.0013, y:0.0097, z:0.0311, w:0.9995},{x: 0.0106, y:-0.0001, z:-0.0056, w:0.9999},{x: -0.0346, y:-0.0002, z:0.0061, w:0.9994}],
|
||||
middle:[{x: -0.001, y:0, z:0.0285, w:0.9996},{x: 0.0106, y:0.0001, z:0.0062, w:0.9999},{x: -0.0346, y:0.0003, z:-0.0074, w:0.9994}],
|
||||
index:[{x: -0.001, y:0, z:-0.0199, w:0.9998},{x: 0.0106, y:-0.0001, z:-0.0079, w:0.9999},{x: -0.0346, y:-0.0008, z:0.0229, w:0.9991}],
|
||||
thumb:[{x: 0.0355, y:-0.0363, z:-0.3263, w:0.9439},{x: -0.0946, y:-0.0938, z:-0.0996, w:0.9861},{x: -0.0952, y:-0.0719, z:-0.1376, w:0.9833}]
|
||||
pinky: [
|
||||
{x: -0.0034, y: 0.023, z: 0.1051, w: 0.9942},
|
||||
{x: 0.0106, y: -0.0001, z: -0.0091, w: 0.9999},
|
||||
{x: -0.0346, y: -0.0003, z: 0.0075, w: 0.9994}
|
||||
],
|
||||
ring: [
|
||||
{x: -0.0013, y: 0.0097, z: 0.0311, w: 0.9995},
|
||||
{x: 0.0106, y: -0.0001, z: -0.0056, w: 0.9999},
|
||||
{x: -0.0346, y: -0.0002, z: 0.0061, w: 0.9994}
|
||||
],
|
||||
middle: [
|
||||
{x: -0.001, y: 0, z: 0.0285, w: 0.9996},
|
||||
{x: 0.0106, y: 0.0001, z: 0.0062, w: 0.9999},
|
||||
{x: -0.0346, y: 0.0003, z: -0.0074, w: 0.9994}
|
||||
],
|
||||
index: [
|
||||
{x: -0.001, y: 0, z: -0.0199, w: 0.9998},
|
||||
{x: 0.0106, y: -0.0001, z: -0.0079, w: 0.9999},
|
||||
{x: -0.0346, y: -0.0008, z: 0.0229, w: 0.9991}
|
||||
],
|
||||
thumb: [
|
||||
{x: 0.0355, y: -0.0363, z: -0.3263, w: 0.9439},
|
||||
{x: -0.0946, y: -0.0938, z: -0.0996, w: 0.9861},
|
||||
{x: -0.0952, y: -0.0719, z: -0.1376, w: 0.9833}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// joint data for close pose
|
||||
var dataClose = {
|
||||
left: {
|
||||
pinky:[{x: 0.5878, y:-0.1735, z:-0.1123, w:0.7821},{x: 0.5704, y:0.0053, z:0.0076, w:0.8213},{x: 0.6069, y:-0.0044, z:-0.0058, w:0.7947}],
|
||||
ring:[{x: 0.5761, y:-0.0989, z:-0.1025, w:0.8048},{x: 0.5332, y:0.0032, z:0.005, w:0.846},{x: 0.5773, y:-0.0035, z:-0.0049, w:0.8165}],
|
||||
middle:[{x: 0.543, y:-0.0469, z:-0.0333, w:0.8378},{x: 0.5419, y:-0.0034, z:-0.0053, w:0.8404},{x: 0.5015, y:0.0037, z:0.0063, w:0.8651}],
|
||||
index:[{x: 0.3051, y:-0.0156, z:-0.014, w:0.9521},{x: 0.6414, y:0.0051, z:0.0063, w:0.7671},{x: 0.5646, y:-0.013, z:-0.019, w:0.8251}],
|
||||
thumb:[{x: 0.313, y:-0.0348, z:0.3192, w:0.8938},{x: 0, y:0, z:-0.37, w:0.929},{x: 0, y:0, z:-0.2604, w:0.9655}]
|
||||
pinky: [
|
||||
{x: 0.5878, y: -0.1735, z: -0.1123, w: 0.7821},
|
||||
{x: 0.5704, y: 0.0053, z: 0.0076, w: 0.8213},
|
||||
{x: 0.6069, y: -0.0044, z: -0.0058, w: 0.7947}
|
||||
],
|
||||
ring: [
|
||||
{x: 0.5761, y: -0.0989, z: -0.1025, w: 0.8048},
|
||||
{x: 0.5332, y: 0.0032, z: 0.005, w: 0.846},
|
||||
{x: 0.5773, y: -0.0035, z: -0.0049, w: 0.8165}
|
||||
],
|
||||
middle: [
|
||||
{x: 0.543, y: -0.0469, z: -0.0333, w: 0.8378},
|
||||
{x: 0.5419, y: -0.0034, z: -0.0053, w: 0.8404},
|
||||
{x: 0.5015, y: 0.0037, z: 0.0063, w: 0.8651}
|
||||
],
|
||||
index: [
|
||||
{x: 0.3051, y: -0.0156, z: -0.014, w: 0.9521},
|
||||
{x: 0.6414, y: 0.0051, z: 0.0063, w: 0.7671},
|
||||
{x: 0.5646, y: -0.013, z: -0.019, w: 0.8251}
|
||||
],
|
||||
thumb: [
|
||||
{x: 0.313, y: -0.0348, z: 0.3192, w: 0.8938},
|
||||
{x: 0, y: 0, z: -0.37, w: 0.929},
|
||||
{x: 0, y: 0, z: -0.2604, w: 0.9655}
|
||||
]
|
||||
}, right: {
|
||||
pinky:[{x: 0.5881, y:0.1728, z:0.1114, w:0.7823},{x: 0.5704, y:-0.0052, z:-0.0075, w:0.8213},{x: 0.6069, y:0.0046, z:0.006, w:0.7947}],
|
||||
ring:[{x: 0.5729, y:0.1181, z:0.0898, w:0.8061},{x: 0.5332, y:-0.003, z:-0.0048, w:0.846},{x: 0.5773, y:0.0035, z:0.005, w:0.8165}],
|
||||
middle:[{x: 0.543, y:0.0468, z:0.0332, w:0.8378},{x: 0.5419, y:0.0034, z:0.0052, w:0.8404},{x: 0.5047, y:-0.0037, z:-0.0064, w:0.8632}],
|
||||
index:[{x: 0.306, y:-0.0076, z:-0.0584, w:0.9502},{x: 0.6409, y:-0.005, z:-0.006, w:0.7675},{x: 0.5646, y:0.0129, z:0.0189, w:0.8251}],
|
||||
thumb:[{x: 0.313, y:0.0352, z:-0.3181, w:0.8942},{x: 0, y:0, z:0.3698, w:0.9291},{x: 0, y:0, z:0.2609, w:0.9654}]
|
||||
pinky: [
|
||||
{x: 0.5881, y: 0.1728, z: 0.1114, w: 0.7823},
|
||||
{x: 0.5704, y: -0.0052, z: -0.0075, w: 0.8213},
|
||||
{x: 0.6069, y: 0.0046, z: 0.006, w: 0.7947}
|
||||
],
|
||||
ring: [
|
||||
{x: 0.5729, y: 0.1181, z: 0.0898, w: 0.8061},
|
||||
{x: 0.5332, y: -0.003, z: -0.0048, w: 0.846},
|
||||
{x: 0.5773, y: 0.0035, z: 0.005, w: 0.8165}
|
||||
],
|
||||
middle: [
|
||||
{x: 0.543, y: 0.0468, z: 0.0332, w: 0.8378},
|
||||
{x: 0.5419, y: 0.0034, z: 0.0052, w: 0.8404},
|
||||
{x: 0.5047, y: -0.0037, z: -0.0064, w: 0.8632}
|
||||
],
|
||||
index: [
|
||||
{x: 0.306, y: -0.0076, z: -0.0584, w: 0.9502},
|
||||
{x: 0.6409, y: -0.005, z: -0.006, w: 0.7675},
|
||||
{x: 0.5646, y: 0.0129, z: 0.0189, w: 0.8251}
|
||||
],
|
||||
thumb: [
|
||||
{x: 0.313, y: 0.0352, z: -0.3181, w: 0.8942},
|
||||
{x: 0, y: 0, z: 0.3698, w: 0.9291},
|
||||
{x: 0, y: 0, z: 0.2609, w: 0.9654}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// snapshot for the default pose
|
||||
|
||||
var dataDefault = {
|
||||
left:{
|
||||
pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
left: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
set: false
|
||||
},
|
||||
right:{
|
||||
pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
right: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
|
@ -127,17 +207,16 @@
|
|||
};
|
||||
|
||||
// joint data for the current frame
|
||||
|
||||
var dataCurrent = {
|
||||
left:{
|
||||
pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
left: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}]
|
||||
},
|
||||
right:{
|
||||
pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
right: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
|
@ -146,17 +225,16 @@
|
|||
};
|
||||
|
||||
// interpolated values on joint data to smooth movement
|
||||
|
||||
var dataDelta = {
|
||||
left:{
|
||||
pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
left: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}]
|
||||
},
|
||||
right:{
|
||||
pinky:[{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
right: {
|
||||
pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}],
|
||||
|
@ -165,35 +243,30 @@
|
|||
};
|
||||
|
||||
// Acquire an updated value per hand every 5 frames when finger is touching (faster in)
|
||||
|
||||
var touchAnimationSteps = 5;
|
||||
|
||||
// Acquire an updated value per hand every 10 frames when finger is returning to default position (slower out)
|
||||
|
||||
// Acquire an updated value per hand every 20 frames when finger is returning to default position (slower out)
|
||||
var defaultAnimationSteps = 10;
|
||||
|
||||
// Debugging info
|
||||
|
||||
var showSphere = false;
|
||||
var showLines = false;
|
||||
|
||||
// This get setup on creation
|
||||
|
||||
var linesCreated = false;
|
||||
var sphereCreated = false;
|
||||
|
||||
// Register object with API Debugger
|
||||
|
||||
var varsToDebug = {
|
||||
scriptLoaded: false,
|
||||
toggleDebugSphere: function(){
|
||||
toggleDebugSphere: function() {
|
||||
showSphere = !showSphere;
|
||||
if (showSphere && !sphereCreated) {
|
||||
createDebugSphere();
|
||||
sphereCreated = true;
|
||||
}
|
||||
},
|
||||
toggleDebugLines: function(){
|
||||
toggleDebugLines: function() {
|
||||
showLines = !showLines;
|
||||
if (showLines && !linesCreated) {
|
||||
createDebugLines();
|
||||
|
@ -228,16 +301,17 @@
|
|||
left: new Palm(),
|
||||
right: new Palm()
|
||||
},
|
||||
offset: {x:0, y:0, z:0},
|
||||
offset: {x: 0, y: 0, z: 0},
|
||||
avatarLoaded: false
|
||||
};
|
||||
|
||||
|
||||
// Add/Subtract the joint data - per finger joint
|
||||
|
||||
function addVals(val1, val2, sign) {
|
||||
var val = [];
|
||||
if (val1.length != val2.length) return;
|
||||
if (val1.length !== val2.length) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < val1.length; i++) {
|
||||
val.push({x: 0, y: 0, z: 0, w: 0});
|
||||
val[i].x = val1[i].x + sign*val2[i].x;
|
||||
|
@ -249,7 +323,6 @@
|
|||
}
|
||||
|
||||
// Multiply/Divide the joint data - per finger joint
|
||||
|
||||
function multiplyValsBy(val1, num) {
|
||||
var val = [];
|
||||
for (var i = 0; i < val1.length; i++) {
|
||||
|
@ -263,7 +336,6 @@
|
|||
}
|
||||
|
||||
// Calculate the finger lengths by adding its joint lengths
|
||||
|
||||
function getJointDistances(jointNamesArray) {
|
||||
var result = {distances: [], totalDistance: 0};
|
||||
for (var i = 1; i < jointNamesArray.length; i++) {
|
||||
|
@ -282,12 +354,13 @@
|
|||
|
||||
var handJoint = handJointNames[side];
|
||||
var jointIndex = MyAvatar.getJointIndex(handJoint);
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint({x:0, y:0, z:0}, jointIndex);
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint({x: 0, y: 0, z: 0}, jointIndex);
|
||||
|
||||
dataOut.position = MyAvatar.jointToWorldPoint(dataIn.position, jointIndex);
|
||||
// dataOut.perpendicular = Vec3.subtract(MyAvatar.jointToWorldPoint(dataIn.perpendicular, jointIndex), worldPosHand);
|
||||
var localPerpendicular = side == "right" ? {x:0.2, y:0, z:1} : {x:-0.2, y:0, z:1};
|
||||
dataOut.perpendicular = Vec3.normalize(Vec3.subtract(MyAvatar.jointToWorldPoint(localPerpendicular, jointIndex), worldPosHand));
|
||||
var localPerpendicular = side === "right" ? {x: 0.2, y: 0, z: 1} : {x: -0.2, y: 0, z: 1};
|
||||
dataOut.perpendicular = Vec3.normalize(
|
||||
Vec3.subtract(MyAvatar.jointToWorldPoint(localPerpendicular, jointIndex), worldPosHand)
|
||||
);
|
||||
dataOut.distance = dataIn.distance;
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
|
@ -299,7 +372,7 @@
|
|||
|
||||
var handJoint = handJointNames[side];
|
||||
var jointIndex = MyAvatar.getJointIndex(handJoint);
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint({x:0, y:0, z:0}, jointIndex);
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint({x: 0, y: 0, z: 0}, jointIndex);
|
||||
|
||||
dataOut.position = MyAvatar.worldToJointPoint(dataIn.position, jointIndex);
|
||||
dataOut.perpendicular = MyAvatar.worldToJointPoint(Vec3.sum(worldPosHand, dataIn.perpendicular), jointIndex);
|
||||
|
@ -310,7 +383,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Calculate the sphere that look up for entities, the center of the palm, perpendicular vector from the palm plane and origin of the the finger rays
|
||||
// Calculate touch field; Sphere at the center of the palm,
|
||||
// perpendicular vector from the palm plane and origin of the the finger rays
|
||||
|
||||
function estimatePalmData(side) {
|
||||
// Return data object
|
||||
|
@ -323,7 +397,7 @@
|
|||
|
||||
// Store position of the hand joint
|
||||
var worldPosHand = MyAvatar.jointToWorldPoint(jointOffset, jointIndexHand);
|
||||
var minusWorldPosHand = {x:-worldPosHand.x, y:-worldPosHand.y, z:-worldPosHand.z};
|
||||
var minusWorldPosHand = {x: -worldPosHand.x, y: -worldPosHand.y, z: -worldPosHand.z};
|
||||
|
||||
// Data for finger rays
|
||||
var directions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined};
|
||||
|
@ -337,7 +411,7 @@
|
|||
var handJointWeight = 1;
|
||||
var fingerJointWeight = 2;
|
||||
|
||||
var palmCenter = {x:0, y:0, z:0};
|
||||
var palmCenter = {x: 0, y: 0, z: 0};
|
||||
palmCenter = Vec3.sum(worldPosHand, palmCenter);
|
||||
|
||||
weightCount += handJointWeight;
|
||||
|
@ -352,7 +426,7 @@
|
|||
positions[finger] = MyAvatar.jointToWorldPoint(jointOffset, jointIndex);
|
||||
directions[finger] = Vec3.normalize(Vec3.sum(positions[finger], minusWorldPosHand));
|
||||
data.fingers[finger] = Vec3.sum(positions[finger], Vec3.multiply(fingerLength, directions[finger]));
|
||||
if (finger != "thumb") {
|
||||
if (finger !== "thumb") {
|
||||
// finger joints have double the weight than the hand joint
|
||||
// This would better position the palm estimation
|
||||
|
||||
|
@ -364,23 +438,25 @@
|
|||
}
|
||||
|
||||
// perpendicular change direction depending on the side
|
||||
|
||||
data.perpendicular = (side == "right") ?
|
||||
Vec3.normalize(Vec3.cross(directions.index, directions.pinky)):
|
||||
Vec3.normalize(Vec3.cross(directions.pinky, directions.index));
|
||||
data.perpendicular = (side === "right") ?
|
||||
Vec3.normalize(Vec3.cross(directions.index, directions.pinky)):
|
||||
Vec3.normalize(Vec3.cross(directions.pinky, directions.index));
|
||||
|
||||
data.position = Vec3.multiply(1.0/weightCount, palmCenter);
|
||||
|
||||
if (side == "right") varsToDebug.offset = MyAvatar.worldToJointPoint(worldPosHand, jointIndexHand);
|
||||
if (side === "right") {
|
||||
varsToDebug.offset = MyAvatar.worldToJointPoint(worldPosHand, jointIndexHand);
|
||||
}
|
||||
|
||||
var palmDistanceMultiplier = 1.55; // 1.55 based on test/error for the sphere radius that best fits the hand
|
||||
data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions.index);
|
||||
|
||||
// move back thumb ray origin
|
||||
var thumbBackMultiplier = 0.2;
|
||||
data.fingers.thumb = Vec3.sum(data.fingers.thumb, Vec3.multiply( -thumbBackMultiplier * thumbLength, data.perpendicular));
|
||||
data.fingers.thumb = Vec3.sum(
|
||||
data.fingers.thumb, Vec3.multiply( -thumbBackMultiplier * thumbLength, data.perpendicular));
|
||||
|
||||
//return getDataRelativeToHandJoint(side, data);
|
||||
// return getDataRelativeToHandJoint(side, data);
|
||||
dataRelativeToHandJoint(side, data, palmData[side]);
|
||||
palmData[side].set = true;
|
||||
// return palmData[side];
|
||||
|
@ -389,19 +465,16 @@
|
|||
// Register GlobalDebugger for API Debugger
|
||||
Script.registerValue("GlobalDebugger", varsToDebug);
|
||||
|
||||
|
||||
|
||||
// store the rays for the fingers - only for debug purposes
|
||||
|
||||
var fingerRays = {
|
||||
left:{
|
||||
left: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
index: undefined
|
||||
},
|
||||
right:{
|
||||
right: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
|
@ -419,14 +492,14 @@
|
|||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
fingerRays.left[fingerKeys[i]] = Overlays.addOverlay("line3d", {
|
||||
color: { red: 0, green: 0, blue: 255 },
|
||||
start: { x:0, y:0, z:0 },
|
||||
end: { x:0, y:1, z:0 },
|
||||
start: { x: 0, y: 0, z: 0 },
|
||||
end: { x: 0, y: 1, z: 0 },
|
||||
visible: showLines
|
||||
});
|
||||
fingerRays.right[fingerKeys[i]] = Overlays.addOverlay("line3d", {
|
||||
color: { red: 0, green: 0, blue: 255 },
|
||||
start: { x:0, y:0, z:0 },
|
||||
end: { x:0, y:1, z:0 },
|
||||
start: { x: 0, y: 0, z: 0 },
|
||||
end: { x: 0, y: 1, z: 0 },
|
||||
visible: showLines
|
||||
});
|
||||
}
|
||||
|
@ -434,14 +507,14 @@
|
|||
palmRay = {
|
||||
left: Overlays.addOverlay("line3d", {
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
start: { x:0, y:0, z:0 },
|
||||
end: { x:0, y:1, z:0 },
|
||||
start: { x: 0, y: 0, z: 0 },
|
||||
end: { x: 0, y: 1, z: 0 },
|
||||
visible: showLines
|
||||
}),
|
||||
right: Overlays.addOverlay("line3d", {
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
start: { x:0, y:0, z:0 },
|
||||
end: { x:0, y:1, z:0 },
|
||||
start: { x: 0, y: 0, z: 0 },
|
||||
end: { x: 0, y: 1, z: 0 },
|
||||
visible: showLines
|
||||
})
|
||||
};
|
||||
|
@ -481,6 +554,98 @@
|
|||
dataDefault[side].set = true;
|
||||
}
|
||||
|
||||
var rayPicks = {
|
||||
left: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
index: undefined
|
||||
},
|
||||
right: {
|
||||
pinky: undefined,
|
||||
middle: undefined,
|
||||
ring: undefined,
|
||||
thumb: undefined,
|
||||
index: undefined
|
||||
}
|
||||
};
|
||||
|
||||
var dataFailed = {
|
||||
left: {
|
||||
pinky: 0,
|
||||
middle: 0,
|
||||
ring: 0,
|
||||
thumb: 0,
|
||||
index: 0
|
||||
},
|
||||
right: {
|
||||
pinky: 0,
|
||||
middle: 0,
|
||||
ring: 0,
|
||||
thumb: 0,
|
||||
index: 0
|
||||
}
|
||||
};
|
||||
|
||||
function clearRayPicks(side) {
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
if (rayPicks[side][finger] !== undefined) {
|
||||
RayPick.removeRayPick(rayPicks[side][finger]);
|
||||
rayPicks[side][finger] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createRayPicks(side) {
|
||||
var data = palmData[side];
|
||||
clearRayPicks(side);
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
var LOOKUP_DISTANCE_MULTIPLIER = 1.5;
|
||||
var dist = LOOKUP_DISTANCE_MULTIPLIER*data.distance;
|
||||
console.log("distance: " + dist);
|
||||
var checkOffset = {
|
||||
x: data.perpendicular.x * dist,
|
||||
y: data.perpendicular.y * dist,
|
||||
z: data.perpendicular.z * dist
|
||||
};
|
||||
|
||||
var checkPoint = Vec3.sum(data.position, Vec3.multiply(2, checkOffset));
|
||||
var sensorToWorldScale = MyAvatar.getSensorToWorldScale();
|
||||
|
||||
var origin = data.fingers[finger];
|
||||
|
||||
var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin));
|
||||
|
||||
origin = Vec3.multiply(1/sensorToWorldScale, origin);
|
||||
|
||||
rayPicks[side][finger] = RayPick.createRayPick(
|
||||
{
|
||||
"enabled": false,
|
||||
"joint": handJointNames[side],
|
||||
"posOffset": origin,
|
||||
"dirOffset": direction,
|
||||
"filter": RayPick.PICK_ENTITIES
|
||||
}
|
||||
);
|
||||
|
||||
RayPick.setPrecisionPicking(rayPicks[side][finger], true);
|
||||
}
|
||||
}
|
||||
function activateNextRay(side, index) {
|
||||
var nextIndex = (index < fingerKeys.length-1) ? index + 1 : 0;
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
var finger = fingerKeys[i];
|
||||
if (i === nextIndex) {
|
||||
RayPick.enableRayPick(rayPicks[side][finger]);
|
||||
} else {
|
||||
RayPick.disableRayPick(rayPicks[side][finger]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateSphereHand(side) {
|
||||
|
||||
var data = new Palm();
|
||||
|
@ -493,11 +658,12 @@
|
|||
|
||||
// Situate the debugging overlays
|
||||
|
||||
var checkOffset = { x: data.perpendicular.x * dist,
|
||||
y: data.perpendicular.y * dist,
|
||||
z: data.perpendicular.z * dist };
|
||||
|
||||
|
||||
var checkOffset = {
|
||||
x: data.perpendicular.x * dist,
|
||||
y: data.perpendicular.y * dist,
|
||||
z: data.perpendicular.z * dist
|
||||
};
|
||||
|
||||
var spherePos = Vec3.sum(palmPoint, checkOffset);
|
||||
var checkPoint = Vec3.sum(palmPoint, Vec3.multiply(2, checkOffset));
|
||||
|
||||
|
@ -529,21 +695,32 @@
|
|||
}
|
||||
|
||||
// Update the intersection of only one finger at a time
|
||||
|
||||
var finger = fingerKeys[updateFingerWithIndex];
|
||||
|
||||
var finger = fingerKeys[updateFingerWithIndex];
|
||||
|
||||
|
||||
var grabbables = Entities.findEntities(spherePos, dist);
|
||||
var newFingerData = dataDefault[side][finger];
|
||||
var animationSteps = defaultAnimationSteps;
|
||||
|
||||
var intersection;
|
||||
if (rayPicks[side][finger] !== undefined) {
|
||||
intersection = RayPick.getPrevRayPickResult(rayPicks[side][finger]);
|
||||
}
|
||||
|
||||
var animationSteps = defaultAnimationSteps;
|
||||
var newFingerData = dataDefault[side][finger];
|
||||
var isAbleToGrab = false;
|
||||
if (grabbables.length > 0) {
|
||||
var origin = data.fingers[finger];
|
||||
var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin));
|
||||
var intersection = Entities.findRayIntersection({origin: origin, direction: direction}, true, grabbables, [], true, false);
|
||||
|
||||
RayPick.setIncludeItems(rayPicks[side][finger], grabbables);
|
||||
|
||||
if (intersection === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var percent = 0; // Initialize
|
||||
var isAbleToGrab = intersection.intersects && intersection.distance < LOOKUP_DISTANCE_MULTIPLIER*dist;
|
||||
isAbleToGrab = intersection.intersects && intersection.distance < LOOKUP_DISTANCE_MULTIPLIER*dist;
|
||||
if (isAbleToGrab && !getTouching(side)) {
|
||||
acquireDefaultPose(side); // take a snapshot of the default pose before touch starts
|
||||
acquireDefaultPose(side); // take a snapshot of the default pose before touch starts
|
||||
newFingerData = dataDefault[side][finger]; // assign default pose to finger data
|
||||
}
|
||||
// Store if this finger is touching something
|
||||
|
@ -558,20 +735,30 @@
|
|||
var THUMB_FACTOR = 0.2;
|
||||
var FINGER_FACTOR = 0.05;
|
||||
|
||||
var grabMultiplier = finger === "thumb" ? THUMB_FACTOR : FINGER_FACTOR; // Amount of grab coefficient added to the fingers - thumb is higher
|
||||
// Amount of grab coefficient added to the fingers - thumb is higher
|
||||
var grabMultiplier = finger === "thumb" ? THUMB_FACTOR : FINGER_FACTOR;
|
||||
percent += grabMultiplier * grabPercent[side];
|
||||
|
||||
// Calculate new interpolation data
|
||||
var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1);
|
||||
newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); // assign close/open ratio to finger to simulate touch
|
||||
// Assign close/open ratio to finger to simulate touch
|
||||
newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1);
|
||||
animationSteps = touchAnimationSteps;
|
||||
}
|
||||
varsToDebug.fingerPercent[side][finger] = percent;
|
||||
}
|
||||
|
||||
// Calculate animation increments
|
||||
dataDelta[side][finger] = multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps);
|
||||
|
||||
|
||||
}
|
||||
if (!isAbleToGrab) {
|
||||
dataFailed[side][finger] = dataFailed[side][finger] === 0 ? 1 : 2;
|
||||
} else {
|
||||
dataFailed[side][finger] = 0;
|
||||
}
|
||||
// If it only fails once it will not update increments
|
||||
if (dataFailed[side][finger] !== 1) {
|
||||
// Calculate animation increments
|
||||
dataDelta[side][finger] =
|
||||
multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps);
|
||||
}
|
||||
}
|
||||
|
||||
// Recreate the finger joint names
|
||||
|
@ -643,31 +830,39 @@
|
|||
}
|
||||
|
||||
function reEstimatePalmData() {
|
||||
["right", "left"].forEach(function(side){
|
||||
["right", "left"].forEach(function(side) {
|
||||
estimatePalmData(side);
|
||||
});
|
||||
}
|
||||
|
||||
function recreateRayPicks() {
|
||||
["right", "left"].forEach(function(side) {
|
||||
createRayPicks(side);
|
||||
});
|
||||
}
|
||||
|
||||
MyAvatar.onLoadComplete.connect(function () {
|
||||
// Sometimes the rig is not ready when this signal is trigger
|
||||
console.log("avatar loaded");
|
||||
Script.setInterval(function(){
|
||||
Script.setTimeout(function() {
|
||||
reEstimatePalmData();
|
||||
}, 2000);
|
||||
recreateRayPicks();
|
||||
}, MSECONDS_AFTER_LOAD);
|
||||
});
|
||||
|
||||
MyAvatar.sensorToWorldScaleChanged.connect(function(){
|
||||
MyAvatar.sensorToWorldScaleChanged.connect(function() {
|
||||
reEstimatePalmData();
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
["right", "left"].forEach(function(side){
|
||||
["right", "left"].forEach(function(side) {
|
||||
if (linesCreated) {
|
||||
Overlays.deleteOverlay(palmRay[side]);
|
||||
}
|
||||
if (sphereCreated) {
|
||||
Overlays.deleteOverlay(sphereHand[side]);
|
||||
}
|
||||
clearRayPicks(side);
|
||||
for (var i = 0; i < fingerKeys.length; i++) {
|
||||
|
||||
var finger = fingerKeys[i];
|
||||
|
@ -684,27 +879,24 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
Script.update.connect(function(){
|
||||
Script.update.connect(function() {
|
||||
|
||||
// index of the finger that needs to be updated this frame
|
||||
|
||||
|
||||
|
||||
updateFingerWithIndex = (updateFingerWithIndex < fingerKeys.length-1) ? updateFingerWithIndex + 1 : 0;
|
||||
|
||||
|
||||
["right", "left"].forEach(function(side){
|
||||
|
||||
["right", "left"].forEach(function(side) {
|
||||
|
||||
if (!palmData[side].set) {
|
||||
reEstimatePalmData();
|
||||
recreateRayPicks();
|
||||
}
|
||||
|
||||
// recalculate the base data
|
||||
updateSphereHand(side);
|
||||
activateNextRay(side, updateFingerWithIndex);
|
||||
|
||||
// this vars manage the transition to default pose
|
||||
var isHandTouching = getTouching(side);
|
||||
|
@ -725,7 +917,8 @@
|
|||
for (var j = 0; j < names.length; j++) {
|
||||
var index = MyAvatar.getJointIndex(names[j]);
|
||||
// if no finger is touching restate the default poses
|
||||
if (isHandTouching || (dataDefault[side].set && countToDefault[side] < 5*touchAnimationSteps)) {
|
||||
if (isHandTouching || (dataDefault[side].set &&
|
||||
countToDefault[side] < fingerKeys.length*touchAnimationSteps)) {
|
||||
var quatRot = dataCurrent[side][finger][j];
|
||||
MyAvatar.setJointRotation(index, quatRot);
|
||||
} else {
|
||||
|
@ -735,5 +928,4 @@
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
}());
|
||||
|
|
|
@ -306,13 +306,21 @@
|
|||
// change pricing to GET/BUY on button hover
|
||||
$('body').on('mouseenter', '#price-or-edit .price', function () {
|
||||
var $this = $(this);
|
||||
var buyString = "BUY";
|
||||
var getString = "GET";
|
||||
// Protection against the button getting stuck in the "BUY"/"GET" state.
|
||||
// That happens when the browser gets two MOUSEENTER events before getting a
|
||||
// MOUSELEAVE event.
|
||||
if ($this.text() === buyString || $this.text() === getString) {
|
||||
return;
|
||||
}
|
||||
$this.data('initialHtml', $this.html());
|
||||
|
||||
var cost = $(this).parent().siblings().text();
|
||||
if (parseInt(cost) > 0) {
|
||||
$this.text('BUY');
|
||||
$this.text(buyString);
|
||||
} else {
|
||||
$this.text('GET');
|
||||
$this.text(getString);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ var HOVER_TEXTURES = {
|
|||
var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6};
|
||||
var SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29};
|
||||
var HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now
|
||||
var conserveResources = true;
|
||||
|
||||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
|
@ -431,7 +430,7 @@ function addAvatarNode(id) {
|
|||
alpha: 0.8,
|
||||
color: color(selected, false, 0.0),
|
||||
ignoreRayIntersection: false
|
||||
}, selected, !conserveResources);
|
||||
}, selected, true);
|
||||
}
|
||||
// Each open/refresh will capture a stable set of avatarsOfInterest, within the specified filter.
|
||||
var avatarsOfInterest = {};
|
||||
|
@ -496,7 +495,6 @@ function populateNearbyUserList(selectData, oldAudioData) {
|
|||
print('PAL data:', JSON.stringify(avatarPalDatum));
|
||||
});
|
||||
getConnectionData(false, location.domainID); // Even admins don't get relationship data in requestUsernameFromID (which is still needed for admin status, which comes from domain).
|
||||
conserveResources = Object.keys(avatarsOfInterest).length > 20;
|
||||
sendToQml({ method: 'nearbyUsers', params: data });
|
||||
if (selectData) {
|
||||
selectData[2] = true;
|
||||
|
@ -719,7 +717,7 @@ function onTabletScreenChanged(type, url) {
|
|||
ContextOverlay.enabled = false;
|
||||
Users.requestsDomainListData = true;
|
||||
|
||||
audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||
audioTimer = createAudioInterval(AUDIO_LEVEL_UPDATE_INTERVAL_MS);
|
||||
|
||||
tablet.tabletShownChanged.connect(tabletVisibilityChanged);
|
||||
Script.update.connect(updateOverlays);
|
||||
|
@ -874,7 +872,6 @@ startup();
|
|||
var isWired = false;
|
||||
var audioTimer;
|
||||
var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too)
|
||||
var AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS = 300;
|
||||
function off() {
|
||||
if (isWired) {
|
||||
Script.update.disconnect(updateOverlays);
|
||||
|
|
|
@ -37,8 +37,8 @@ var shareAfterLogin = false;
|
|||
var snapshotToShareAfterLogin = [];
|
||||
var METAVERSE_BASE = Account.metaverseServerURL;
|
||||
var isLoggedIn;
|
||||
var numGifSnapshotUploadsPending = 0;
|
||||
var numStillSnapshotUploadsPending = 0;
|
||||
var mostRecentGifSnapshotFilename = "";
|
||||
var mostRecentStillSnapshotFilename = "";
|
||||
|
||||
// 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
|
||||
|
@ -64,6 +64,10 @@ function fileExtensionMatches(filePath, extension) {
|
|||
return filePath.split('.').pop().toLowerCase() === extension;
|
||||
}
|
||||
|
||||
function getFilenameFromPath(str) {
|
||||
return str.split('\\').pop().split('/').pop();
|
||||
}
|
||||
|
||||
function onMessage(message) {
|
||||
// Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following:
|
||||
// 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)
|
||||
|
@ -147,9 +151,9 @@ function onMessage(message) {
|
|||
print('Sharing snapshot with audience "for_url":', message.data);
|
||||
Window.shareSnapshot(message.data, Settings.getValue("previousSnapshotHref"));
|
||||
if (isGif) {
|
||||
numGifSnapshotUploadsPending++;
|
||||
mostRecentGifSnapshotFilename = getFilenameFromPath(message.data);
|
||||
} else {
|
||||
numStillSnapshotUploadsPending++;
|
||||
mostRecentStillSnapshotFilename = getFilenameFromPath(message.data);
|
||||
}
|
||||
} else {
|
||||
shareAfterLogin = true;
|
||||
|
@ -385,13 +389,11 @@ function snapshotUploaded(isError, reply) {
|
|||
ignoreStillSnapshotData = false;
|
||||
storyIDsToMaybeDelete.push(storyID);
|
||||
if (isGif) {
|
||||
numGifSnapshotUploadsPending--;
|
||||
if (numGifSnapshotUploadsPending !== 0) {
|
||||
if (mostRecentGifSnapshotFilename !== replyJson.user_story.details.original_image_file_name) {
|
||||
ignoreGifSnapshotData = true;
|
||||
}
|
||||
} else {
|
||||
numStillSnapshotUploadsPending--;
|
||||
if (numStillSnapshotUploadsPending !== 0) {
|
||||
if (mostRecentStillSnapshotFilename !== replyJson.user_story.details.original_image_file_name) {
|
||||
ignoreStillSnapshotData = true;
|
||||
}
|
||||
}
|
||||
|
@ -686,9 +688,9 @@ function onUsernameChanged() {
|
|||
Window.shareSnapshot(element.path, element.href);
|
||||
var isGif = fileExtensionMatches(element.path, "gif");
|
||||
if (isGif) {
|
||||
numGifSnapshotUploadsPending++;
|
||||
mostRecentGifSnapshotFilename = getFilenameFromPath(element.path);
|
||||
} else {
|
||||
numStillSnapshotUploadsPending++;
|
||||
mostRecentStillSnapshotFilename = getFilenameFromPath(element.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -75,10 +75,14 @@ function getBuildInfo() {
|
|||
}
|
||||
const buildInfo = getBuildInfo();
|
||||
|
||||
function getRootHifiDataDirectory() {
|
||||
function getRootHifiDataDirectory(local) {
|
||||
var organization = buildInfo.organization;
|
||||
if (osType == 'Windows_NT') {
|
||||
return path.resolve(osHomeDir(), 'AppData/Roaming', organization);
|
||||
if (local) {
|
||||
return path.resolve(osHomeDir(), 'AppData/Local', organization);
|
||||
} else {
|
||||
return path.resolve(osHomeDir(), 'AppData/Roaming', organization);
|
||||
}
|
||||
} else if (osType == 'Darwin') {
|
||||
return path.resolve(osHomeDir(), 'Library/Application Support', organization);
|
||||
} else {
|
||||
|
@ -94,8 +98,8 @@ function getAssignmentClientResourcesDirectory() {
|
|||
return path.join(getRootHifiDataDirectory(), '/assignment-client');
|
||||
}
|
||||
|
||||
function getApplicationDataDirectory() {
|
||||
return path.join(getRootHifiDataDirectory(), '/Server Console');
|
||||
function getApplicationDataDirectory(local) {
|
||||
return path.join(getRootHifiDataDirectory(local), '/Server Console');
|
||||
}
|
||||
|
||||
// Update lock filepath
|
||||
|
@ -104,7 +108,7 @@ const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_F
|
|||
|
||||
// Configure log
|
||||
global.log = require('electron-log');
|
||||
const logFile = getApplicationDataDirectory() + '/log.txt';
|
||||
const logFile = getApplicationDataDirectory(true) + '/log.txt';
|
||||
fs.ensureFileSync(logFile); // Ensure file exists
|
||||
log.transports.file.maxSize = 5 * 1024 * 1024;
|
||||
log.transports.file.file = logFile;
|
||||
|
@ -221,7 +225,19 @@ function deleteOldFiles(directoryPath, maxAgeInSeconds, filenameRegex) {
|
|||
}
|
||||
}
|
||||
|
||||
var logPath = path.join(getApplicationDataDirectory(), '/logs');
|
||||
var oldLogPath = path.join(getApplicationDataDirectory(), '/logs');
|
||||
var logPath = path.join(getApplicationDataDirectory(true), '/logs');
|
||||
|
||||
if (oldLogPath != logPath) {
|
||||
console.log("Migrating old logs from " + oldLogPath + " to " + logPath);
|
||||
fs.copy(oldLogPath, logPath, err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('success!');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
log.debug("Log directory:", logPath);
|
||||
log.debug("Data directory:", getRootHifiDataDirectory());
|
||||
|
@ -433,13 +449,6 @@ var labels = {
|
|||
logWindow.open();
|
||||
}
|
||||
},
|
||||
restoreBackup: {
|
||||
label: 'Restore Backup Instructions',
|
||||
click: function() {
|
||||
var folder = getRootHifiDataDirectory() + "/Server Backup";
|
||||
openBackupInstructions(folder);
|
||||
}
|
||||
},
|
||||
share: {
|
||||
label: 'Share',
|
||||
click: function() {
|
||||
|
@ -475,7 +484,6 @@ function buildMenuArray(serverState) {
|
|||
menuArray.push(labels.stopServer);
|
||||
menuArray.push(labels.settings);
|
||||
menuArray.push(labels.viewLogs);
|
||||
menuArray.push(labels.restoreBackup);
|
||||
menuArray.push(separator);
|
||||
menuArray.push(labels.share);
|
||||
menuArray.push(separator);
|
||||
|
@ -542,103 +550,6 @@ function backupResourceDirectories(folder) {
|
|||
}
|
||||
}
|
||||
|
||||
function openBackupInstructions(folder) {
|
||||
// Explain user how to restore server
|
||||
var window = new BrowserWindow({
|
||||
icon: appIcon,
|
||||
width: 800,
|
||||
height: 520,
|
||||
});
|
||||
window.loadURL('file://' + __dirname + '/content-update.html');
|
||||
if (!debug) {
|
||||
window.setMenu(null);
|
||||
}
|
||||
window.show();
|
||||
|
||||
electron.ipcMain.on('setSize', function(event, obj) {
|
||||
window.setSize(obj.width, obj.height);
|
||||
});
|
||||
electron.ipcMain.on('ready', function() {
|
||||
log.debug("got ready");
|
||||
window.webContents.send('update', folder);
|
||||
});
|
||||
}
|
||||
function backupResourceDirectoriesAndRestart() {
|
||||
homeServer.stop();
|
||||
|
||||
var folder = getRootHifiDataDirectory() + "/Server Backup - " + Date.now();
|
||||
if (backupResourceDirectories(folder)) {
|
||||
maybeInstallDefaultContentSet(onContentLoaded);
|
||||
openBackupInstructions(folder);
|
||||
} else {
|
||||
dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
buttons: ['Ok'],
|
||||
title: 'Update Error',
|
||||
message: 'There was an error updating the content, aborting.'
|
||||
}, function() {});
|
||||
}
|
||||
}
|
||||
|
||||
function checkNewContent() {
|
||||
if (argv.noUpdater) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start downloading content set
|
||||
var req = request.head({
|
||||
url: HOME_CONTENT_URL
|
||||
}, function (error, response, body) {
|
||||
if (error === null) {
|
||||
var localContent = Date.parse(userConfig.get('homeContentLastModified'));
|
||||
var remoteContent = Date.parse(response.headers['last-modified']);
|
||||
|
||||
var shouldUpdate = isNaN(localContent) || (!isNaN(remoteContent) && (remoteContent > localContent));
|
||||
|
||||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
log.debug('Last Modified: ' + response.headers['last-modified']);
|
||||
log.debug(localContent + " " + remoteContent + " " + shouldUpdate + " " + new Date());
|
||||
log.debug("Remote content is " + (shouldUpdate ? "newer" : "older") + " that local content.");
|
||||
}
|
||||
|
||||
if (shouldUpdate) {
|
||||
dialog.showMessageBox({
|
||||
type: 'question',
|
||||
buttons: ['Yes', 'No'],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
title: 'High Fidelity Sandbox',
|
||||
message: 'A newer version of the home content set is available.\nDo you wish to update?',
|
||||
noLink: true,
|
||||
}, function(idx) {
|
||||
if (idx === 0) {
|
||||
dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
buttons: ['Yes', 'No'],
|
||||
defaultId: 1,
|
||||
cancelId: 1,
|
||||
title: 'Are you sure?',
|
||||
message: 'Updating with the new content will remove all your current content and settings and place them in a backup folder.\nAre you sure?',
|
||||
noLink: true,
|
||||
}, function(idx) {
|
||||
if (idx === 0) {
|
||||
backupResourceDirectoriesAndRestart();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// They don't want to update, mark content set as current
|
||||
userConfig.set('homeContentLastModified', new Date());
|
||||
userConfig.save(configPath);
|
||||
}
|
||||
});
|
||||
} else if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) {
|
||||
backupResourceDirectoriesAndRestart();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory) {
|
||||
if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) {
|
||||
log.debug('Removing incomplete content update files before copying new update');
|
||||
|
@ -681,7 +592,6 @@ function maybeInstallDefaultContentSet(onComplete) {
|
|||
log.debug("User has existing data, suppressing downloader");
|
||||
onComplete();
|
||||
|
||||
checkNewContent();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
1
tools/010-templates/README.md
Normal file
1
tools/010-templates/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
This directory contains [010 editor](https://www.sweetscape.com/010editor/) templates for parsing and inspecting different file types.
|
102
tools/010-templates/fbx.bt
Normal file
102
tools/010-templates/fbx.bt
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// fbx.bt
|
||||
// tools/010-templates
|
||||
//
|
||||
// Created by Ryan Huffman
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// FBX file template
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
local char use64BitAddresses = 1;
|
||||
|
||||
struct Header {
|
||||
char prefix[23];
|
||||
int32 version;
|
||||
};
|
||||
|
||||
struct Property {
|
||||
char type;
|
||||
if (type == 'Y') {
|
||||
int16 value;
|
||||
} else if (type == 'C') {
|
||||
char value;
|
||||
} else if (type == 'I') {
|
||||
int32 value;
|
||||
} else if (type == 'F') {
|
||||
float value;
|
||||
} else if (type == 'D') {
|
||||
double value;
|
||||
} else if (type == 'L') {
|
||||
int64 value;
|
||||
} else if (type == 'S' || type == 'R') {
|
||||
uint32 size;
|
||||
char value[size];
|
||||
} else {
|
||||
uint32 length;
|
||||
uint32 encoding;
|
||||
uint32 compressedLength;
|
||||
if (encoding == 1) {
|
||||
char compressedData[compressedLength];
|
||||
} else if (type == 'f') {
|
||||
float values[this.length];
|
||||
} else if (type == 'd') {
|
||||
double values[this.length];
|
||||
} else if (type == 'l') {
|
||||
int64 values[this.length];
|
||||
} else if (type == 'i') {
|
||||
int32 values[this.length];
|
||||
} else if (type == 'b') {
|
||||
char values[this.length];
|
||||
} else {
|
||||
Printf("%c", type);
|
||||
Assert(false, "Error, unknown property type");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Node;
|
||||
|
||||
string nodeName(Node& node) {
|
||||
if (!exists(node.name)) {
|
||||
return "Node ----- ";
|
||||
}
|
||||
local string s;
|
||||
SPrintf(s, "Node (%s) ", node.name);
|
||||
return s;
|
||||
}
|
||||
|
||||
struct Node {
|
||||
if (use64BitAddresses) {
|
||||
int64 endOffset;
|
||||
uint64 propertyCount;
|
||||
uint64 propertyListLength;
|
||||
} else {
|
||||
int32 endOffset;
|
||||
uint32 propertyCount;
|
||||
uint32 propertyListLength;
|
||||
}
|
||||
uchar nameLength;
|
||||
char name[this.nameLength];
|
||||
Property properties[this.propertyCount]<optimize=false>;
|
||||
while (FTell() < endOffset) {
|
||||
Node children<optimize=false, name=nodeName>;
|
||||
}
|
||||
};
|
||||
|
||||
struct File {
|
||||
Header header;
|
||||
use64BitAddresses = header.version >= 7500;
|
||||
local int i = 0;
|
||||
Node node<name=nodeName>;
|
||||
local string name = node.name;
|
||||
while (name != "") {
|
||||
Node node<name=nodeName>;
|
||||
i++;
|
||||
name = exists(node[i].name) ? node[i].name : "";
|
||||
}
|
||||
|
||||
} file;
|
52
tools/010-templates/ktx.bt
Normal file
52
tools/010-templates/ktx.bt
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ktx.bt
|
||||
// tools/010-templates
|
||||
//
|
||||
// Created by Ryan Huffman
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// KTX file template
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
struct Header {
|
||||
char identifier[12];
|
||||
uint32 endianness<format=hex>;
|
||||
uint32 glType;
|
||||
uint32 glTypeSize;
|
||||
uint32 glFormat;
|
||||
uint32 glInternalFormat;
|
||||
uint32 glBaseInternalFormat;
|
||||
uint32 pixelWidth;
|
||||
uint32 pixelHeight;
|
||||
uint32 pixelDepth;
|
||||
uint32 numberOfArrayElements;
|
||||
uint32 numberOfFaces;
|
||||
uint32 numberOfMipmapLevels;
|
||||
uint32 bytesOfKeyValueData;
|
||||
};
|
||||
|
||||
struct KV {
|
||||
uint32 byteSize;
|
||||
local uint32 keyLength = ReadStringLength(FTell());
|
||||
char key[keyLength];
|
||||
char value[byteSize - keyLength] <format=hex>;
|
||||
char padding[3 - ((byteSize + 3) % 4)];
|
||||
};
|
||||
|
||||
string kvName(KV& kv) {
|
||||
local string s;
|
||||
SPrintf(s, "KeyValue (%s) ", kv.key);
|
||||
return s;
|
||||
}
|
||||
|
||||
struct File {
|
||||
Header header;
|
||||
local uint32 endOfKV = FTell() + header.bytesOfKeyValueData;
|
||||
while (FTell() < endOfKV) {
|
||||
KV keyValue <optimize=false, name=kvName>;
|
||||
}
|
||||
char imageData[FileSize() - FTell()];
|
||||
} file;
|
23
tools/dissectors/hf-domain.lua
Normal file
23
tools/dissectors/hf-domain.lua
Normal file
|
@ -0,0 +1,23 @@
|
|||
-- create the domain protocol
|
||||
p_hf_domain = Proto("hf-domain", "HF Domain Protocol")
|
||||
|
||||
-- domain packet fields
|
||||
local f_domain_id = ProtoField.guid("hf_domain.domain_id", "Domain ID")
|
||||
local f_domain_local_id = ProtoField.uint16("hf_domain.domain_local_id", "Domain Local ID")
|
||||
|
||||
p_hf_domain.fields = {
|
||||
f_domain_id, f_domain_local_id
|
||||
}
|
||||
|
||||
function p_hf_domain.dissector(buf, pinfo, tree)
|
||||
pinfo.cols.protocol = p_hf_domain.name
|
||||
|
||||
domain_subtree = tree:add(p_hf_domain, buf())
|
||||
|
||||
local i = 0
|
||||
|
||||
domain_subtree:add(f_domain_id, buf(i, 16))
|
||||
i = i + 16
|
||||
|
||||
domain_subtree:add_le(f_domain_local_id, buf(i, 2))
|
||||
end
|
|
@ -4,11 +4,21 @@ p_hf_entity = Proto("hf-entity", "HF Entity Protocol")
|
|||
-- entity packet fields
|
||||
local f_entity_sequence_number = ProtoField.uint16("hf_entity.sequence_number", "Sequence Number")
|
||||
local f_entity_timestamp = ProtoField.uint64("hf_entity.timestamp", "Timestamp")
|
||||
local f_octal_code_bytes = ProtoField.uint8("hf_entity.octal_code_bytes", "Octal Code Bytes")
|
||||
local f_octal_code_three_bit_sections = ProtoField.uint8("hf_entity.octal_code_three_bit_sections", "Octal Code Three Bit Sections")
|
||||
local f_octal_code = ProtoField.bytes("hf_entity.octal_code", "Octal Code")
|
||||
local f_entity_id = ProtoField.guid("hf_entity.entity_id", "Entity ID")
|
||||
local f_last_edited = ProtoField.uint64("hf_entity.last_edited", "Last Edited")
|
||||
local f_coded_property_type = ProtoField.bytes("hf_entity.coded_property_type", "Coded Property Type")
|
||||
local f_property_type = ProtoField.uint32("hf_entity.property_type", "Property Type")
|
||||
local f_coded_update_delta = ProtoField.bytes("hf_entity.f_coded_update_delta", "Coded Update Delta")
|
||||
local f_update_delta = ProtoField.uint32("hf_entity.update_delta", "Update Delta")
|
||||
|
||||
p_hf_entity.fields = {
|
||||
f_entity_sequence_number, f_entity_timestamp, f_octal_code_bytes, f_entity_id
|
||||
f_entity_sequence_number, f_entity_timestamp,
|
||||
f_octal_code_three_bit_sections, f_octal_code,
|
||||
f_last_edited, f_entity_id,
|
||||
f_coded_property_type, f_property_type,
|
||||
f_coded_update_delta, f_update_delta
|
||||
}
|
||||
|
||||
function p_hf_entity.dissector(buf, pinfo, tree)
|
||||
|
@ -16,21 +26,72 @@ function p_hf_entity.dissector(buf, pinfo, tree)
|
|||
|
||||
entity_subtree = tree:add(p_hf_entity, buf())
|
||||
|
||||
i = 0
|
||||
local i = 0
|
||||
|
||||
entity_subtree:add_le(f_entity_sequence_number, buf(i, 2))
|
||||
i = i + 2
|
||||
|
||||
entity_subtree:add_le(f_entity_timestamp, buf(i, 4))
|
||||
i = i + 4
|
||||
entity_subtree:add_le(f_entity_timestamp, buf(i, 8))
|
||||
i = i + 8
|
||||
|
||||
-- figure out the number of bytes the octal code takes
|
||||
local octal_code_bytes = buf(i, 1):le_uint()
|
||||
entity_subtree:add_le(f_octal_code_bytes, buf(i, 1))
|
||||
-- figure out the number of three bit sections in the octal code
|
||||
local octal_code_three_bit_sections = buf(i, 1):le_uint()
|
||||
entity_subtree:add_le(f_octal_code_three_bit_sections, buf(i, 1))
|
||||
i = i + 1
|
||||
|
||||
-- skip over the octal code
|
||||
i = i + 1 + octal_code_bytes
|
||||
-- read the bytes for the octal code
|
||||
local octal_code_bytes = math.ceil((octal_code_three_bit_sections * 3) / 8)
|
||||
entity_subtree:add_le(f_octal_code, buf(i, octal_code_bytes))
|
||||
i = i + octal_code_bytes
|
||||
|
||||
-- read the last edited timestamp
|
||||
entity_subtree:add_le(f_last_edited, buf(i, 8))
|
||||
i = i + 8
|
||||
|
||||
-- read the entity ID
|
||||
entity_subtree:add(f_entity_id, buf(i, 16))
|
||||
i = i + 16
|
||||
|
||||
-- figure out the property type and the size of the coded value
|
||||
local property_type, coded_property_bytes = number_of_coded_bytes(buf(i))
|
||||
entity_subtree:add(f_coded_property_type, buf(i, coded_property_bytes))
|
||||
entity_subtree:add(f_property_type, property_type)
|
||||
i = i + coded_property_bytes
|
||||
|
||||
-- figure out the update delta and the size of the coded value
|
||||
local update_delta, coded_update_delta_bytes = number_of_coded_bytes(buf(i))
|
||||
entity_subtree:add(f_coded_update_delta, buf(i, coded_update_delta_bytes))
|
||||
entity_subtree:add(f_update_delta, update_delta)
|
||||
i = i + coded_update_delta_bytes
|
||||
end
|
||||
|
||||
function number_of_coded_bytes(buf)
|
||||
local coded_buffer = buf(0, 4):le_uint() -- max 64 bit value means max 10 header bits
|
||||
|
||||
-- first figure out the total number of bytes for the coded value based
|
||||
-- on the bits in the header
|
||||
local total_coded_bytes = 1
|
||||
|
||||
for bit = 0, 10, 1 do
|
||||
local header_bit = bit32.extract(coded_buffer, bit)
|
||||
|
||||
if header_bit == 1 then
|
||||
total_coded_bytes = total_coded_bytes + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- pull out the bits and write them to our decoded value
|
||||
local decoded_value = 0
|
||||
local decoded_position = 0
|
||||
local total_bits = total_coded_bytes * 8
|
||||
|
||||
for bit = total_coded_bytes, total_bits - 1, 1 do
|
||||
local value_bit = bit32.extract(coded_buffer, total_bits - bit - 1)
|
||||
decoded_value = bit32.replace(decoded_value, value_bit, decoded_position)
|
||||
decoded_position = decoded_position + 1
|
||||
end
|
||||
|
||||
return decoded_value, total_coded_bytes
|
||||
end
|
||||
|
|
|
@ -118,6 +118,10 @@ local packet_types = {
|
|||
[54] = "AssetGetInfoReply"
|
||||
}
|
||||
|
||||
local unsourced_packet_types = {
|
||||
["DomainList"] = true
|
||||
}
|
||||
|
||||
function p_hfudt.dissector(buf, pinfo, tree)
|
||||
|
||||
-- make sure this isn't a STUN packet - those don't follow HFUDT format
|
||||
|
@ -230,54 +234,63 @@ function p_hfudt.dissector(buf, pinfo, tree)
|
|||
|
||||
-- if the message bit is set, handle the second word
|
||||
if message_bit == 1 then
|
||||
payload_offset = 12
|
||||
payload_offset = 12
|
||||
|
||||
local second_word = buf(4, 4):le_uint()
|
||||
local second_word = buf(4, 4):le_uint()
|
||||
|
||||
-- read message position from upper 2 bits
|
||||
local message_position = bit32.rshift(second_word, 30)
|
||||
local position = subtree:add(f_message_position, message_position)
|
||||
-- read message position from upper 2 bits
|
||||
local message_position = bit32.rshift(second_word, 30)
|
||||
local position = subtree:add(f_message_position, message_position)
|
||||
|
||||
if message_positions[message_position] ~= nil then
|
||||
-- if we know this position then add the name
|
||||
position:append_text(" (".. message_positions[message_position] .. ")")
|
||||
end
|
||||
if message_positions[message_position] ~= nil then
|
||||
-- if we know this position then add the name
|
||||
position:append_text(" (".. message_positions[message_position] .. ")")
|
||||
end
|
||||
|
||||
-- read message number from lower 30 bits
|
||||
subtree:add(f_message_number, bit32.band(second_word, 0x3FFFFFFF))
|
||||
-- read message number from lower 30 bits
|
||||
subtree:add(f_message_number, bit32.band(second_word, 0x3FFFFFFF))
|
||||
|
||||
-- read the message part number
|
||||
subtree:add(f_message_part_number, buf(8, 4):le_uint())
|
||||
-- read the message part number
|
||||
subtree:add(f_message_part_number, buf(8, 4):le_uint())
|
||||
end
|
||||
|
||||
-- read the type
|
||||
local packet_type = buf(payload_offset, 1):le_uint()
|
||||
local ptype = subtree:add_le(f_type, buf(payload_offset, 1))
|
||||
if packet_types[packet_type] ~= nil then
|
||||
subtree:add(f_type_text, packet_types[packet_type])
|
||||
local packet_type_text = packet_types[packet_type]
|
||||
if packet_type_text ~= nil then
|
||||
subtree:add(f_type_text, packet_type_text)
|
||||
-- if we know this packet type then add the name
|
||||
ptype:append_text(" (".. packet_types[packet_type] .. ")")
|
||||
ptype:append_text(" (".. packet_type_text .. ")")
|
||||
end
|
||||
|
||||
|
||||
-- read the version
|
||||
subtree:add_le(f_version, buf(payload_offset + 1, 1))
|
||||
|
||||
-- read node local ID
|
||||
local sender_id = buf(payload_offset + 2, 2)
|
||||
subtree:add_le(f_sender_id, sender_id)
|
||||
local i = payload_offset + 2
|
||||
|
||||
local i = payload_offset + 4
|
||||
if unsourced_packet_types[packet_type_text] == nil then
|
||||
-- read node local ID
|
||||
local sender_id = buf(payload_offset + 2, 2)
|
||||
subtree:add_le(f_sender_id, sender_id)
|
||||
i = i + 2
|
||||
|
||||
-- read HMAC MD5 hash
|
||||
subtree:add(f_hmac_hash, buf(i, 16))
|
||||
i = i + 16
|
||||
-- read HMAC MD5 hash
|
||||
subtree:add(f_hmac_hash, buf(i, 16))
|
||||
i = i + 16
|
||||
end
|
||||
|
||||
-- Domain packets
|
||||
if packet_type_text == "DomainList" then
|
||||
Dissector.get("hf-domain"):call(buf(i):tvb(), pinfo, tree)
|
||||
end
|
||||
|
||||
-- AvatarData or BulkAvatarDataPacket
|
||||
if packet_types[packet_type] == "AvatarData" or packet_types[packet_type] == "BulkAvatarDataPacket" then
|
||||
if packet_type_text == "AvatarData" or packet_type_text == "BulkAvatarData" then
|
||||
Dissector.get("hf-avatar"):call(buf(i):tvb(), pinfo, tree)
|
||||
end
|
||||
|
||||
if packet_types[packet_type] == "EntityEdit" then
|
||||
if packet_type_text == "EntityEdit" then
|
||||
Dissector.get("hf-entity"):call(buf(i):tvb(), pinfo, tree)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ if (WIN32)
|
|||
elseif (UNIX AND NOT APPLE)
|
||||
find_package(Threads REQUIRED)
|
||||
if(THREADS_HAVE_PTHREAD_ARG)
|
||||
target_compile_options(PUBLIC oven "-pthread")
|
||||
target_compile_options(oven PUBLIC "-pthread")
|
||||
endif()
|
||||
elseif (APPLE)
|
||||
# Fix up the rpath so macdeployqt works
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "OvenCLIApplication.h"
|
||||
#include "ModelBakingLoggingCategory.h"
|
||||
#include "FBXBaker.h"
|
||||
|
@ -38,17 +40,15 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString&
|
|||
static const QString MODEL_EXTENSION { "fbx" };
|
||||
static const QString SCRIPT_EXTENSION { "js" };
|
||||
|
||||
QString extension = type;
|
||||
|
||||
if (extension.isNull()) {
|
||||
auto url = inputUrl.toDisplayString();
|
||||
extension = url.mid(url.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
// check what kind of baker we should be creating
|
||||
bool isFBX = extension == MODEL_EXTENSION;
|
||||
bool isScript = extension == SCRIPT_EXTENSION;
|
||||
bool isFBX = type == MODEL_EXTENSION;
|
||||
bool isScript = type == SCRIPT_EXTENSION;
|
||||
|
||||
// If the type doesn't match the above, we assume we have a texture, and the type specified is the
|
||||
// texture usage type (albedo, cubemap, normals, etc.)
|
||||
auto url = inputUrl.toDisplayString();
|
||||
auto idx = url.lastIndexOf('.');
|
||||
auto extension = idx >= 0 ? url.mid(idx + 1).toLower() : "";
|
||||
bool isSupportedImage = QImageReader::supportedImageFormats().contains(extension.toLatin1());
|
||||
|
||||
_outputPath = outputPath;
|
||||
|
@ -65,7 +65,29 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString&
|
|||
_baker = std::unique_ptr<Baker> { new JSBaker(inputUrl, outputPath) };
|
||||
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||
} else if (isSupportedImage) {
|
||||
_baker = std::unique_ptr<Baker> { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) };
|
||||
static const std::unordered_map<QString, image::TextureUsage::Type> STRING_TO_TEXTURE_USAGE_TYPE_MAP {
|
||||
{ "default", image::TextureUsage::DEFAULT_TEXTURE },
|
||||
{ "strict", image::TextureUsage::STRICT_TEXTURE },
|
||||
{ "albedo", image::TextureUsage::ALBEDO_TEXTURE },
|
||||
{ "normal", image::TextureUsage::NORMAL_TEXTURE },
|
||||
{ "bump", image::TextureUsage::BUMP_TEXTURE },
|
||||
{ "specular", image::TextureUsage::SPECULAR_TEXTURE },
|
||||
{ "metallic", image::TextureUsage::METALLIC_TEXTURE },
|
||||
{ "roughness", image::TextureUsage::ROUGHNESS_TEXTURE },
|
||||
{ "gloss", image::TextureUsage::GLOSS_TEXTURE },
|
||||
{ "emissive", image::TextureUsage::EMISSIVE_TEXTURE },
|
||||
{ "cube", image::TextureUsage::CUBE_TEXTURE },
|
||||
{ "occlusion", image::TextureUsage::OCCLUSION_TEXTURE },
|
||||
{ "scattering", image::TextureUsage::SCATTERING_TEXTURE },
|
||||
{ "lightmap", image::TextureUsage::LIGHTMAP_TEXTURE },
|
||||
};
|
||||
|
||||
auto it = STRING_TO_TEXTURE_USAGE_TYPE_MAP.find(type);
|
||||
if (it == STRING_TO_TEXTURE_USAGE_TYPE_MAP.end()) {
|
||||
qCDebug(model_baking) << "Unknown texture usage type:" << type;
|
||||
QCoreApplication::exit(OVEN_STATUS_CODE_FAIL);
|
||||
}
|
||||
_baker = std::unique_ptr<Baker> { new TextureBaker(inputUrl, it->second, outputPath) };
|
||||
_baker->moveToThread(Oven::instance().getNextWorkerThread());
|
||||
} else {
|
||||
qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl;
|
||||
|
|
|
@ -14,11 +14,14 @@
|
|||
#include <QtCore/QCommandLineParser>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <image/Image.h>
|
||||
|
||||
#include "BakerCLI.h"
|
||||
|
||||
static const QString CLI_INPUT_PARAMETER = "i";
|
||||
static const QString CLI_OUTPUT_PARAMETER = "o";
|
||||
static const QString CLI_TYPE_PARAMETER = "t";
|
||||
static const QString CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER = "disable-texture-compression";
|
||||
|
||||
OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv)
|
||||
|
@ -29,7 +32,8 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) :
|
|||
parser.addOptions({
|
||||
{ CLI_INPUT_PARAMETER, "Path to file that you would like to bake.", "input" },
|
||||
{ CLI_OUTPUT_PARAMETER, "Path to folder that will be used as output.", "output" },
|
||||
{ CLI_TYPE_PARAMETER, "Type of asset.", "type" }
|
||||
{ CLI_TYPE_PARAMETER, "Type of asset.", "type" },
|
||||
{ CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER, "Disable texture compression." }
|
||||
});
|
||||
|
||||
parser.addHelpOption();
|
||||
|
@ -40,6 +44,15 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) :
|
|||
QUrl inputUrl(QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER)));
|
||||
QUrl outputUrl(QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER)));
|
||||
QString type = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString::null;
|
||||
|
||||
if (parser.isSet(CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER)) {
|
||||
qDebug() << "Disabling texture compression";
|
||||
image::setColorTexturesCompressionEnabled(false);
|
||||
image::setGrayscaleTexturesCompressionEnabled(false);
|
||||
image::setNormalTexturesCompressionEnabled(false);
|
||||
image::setCubeTexturesCompressionEnabled(false);
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(cli, "bakeFile", Qt::QueuedConnection, Q_ARG(QUrl, inputUrl),
|
||||
Q_ARG(QString, outputUrl.toString()), Q_ARG(QString, type));
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue