Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Daniela 2017-09-26 17:49:15 +01:00
commit e264503b9f
50 changed files with 526 additions and 207 deletions

View file

@ -953,16 +953,15 @@ void AssetServer::removeBakedPathsForDeletedAsset(AssetHash hash) {
deleteMappings(hiddenBakedFolder); deleteMappings(hiddenBakedFolder);
} }
bool AssetServer::deleteMappings(AssetPathList& paths) { bool AssetServer::deleteMappings(const AssetPathList& paths) {
// take a copy of the current mappings in case persistence of these deletes fails // take a copy of the current mappings in case persistence of these deletes fails
auto oldMappings = _fileMappings; auto oldMappings = _fileMappings;
QSet<QString> hashesToCheckForDeletion; QSet<QString> hashesToCheckForDeletion;
// enumerate the paths to delete and remove them all // enumerate the paths to delete and remove them all
for (auto& path : paths) { for (const auto& rawPath : paths) {
auto path = rawPath.trimmed();
path = path.trimmed();
// figure out if this path will delete a file or folder // figure out if this path will delete a file or folder
if (pathIsFolder(path)) { if (pathIsFolder(path)) {
@ -991,12 +990,12 @@ bool AssetServer::deleteMappings(AssetPathList& paths) {
} else { } else {
auto it = _fileMappings.find(path); auto it = _fileMappings.find(path);
if (it != _fileMappings.end()) { if (it != _fileMappings.end()) {
_fileMappings.erase(it);
// add this hash to the list we need to check for asset removal from server // add this hash to the list we need to check for asset removal from server
hashesToCheckForDeletion << it->second; hashesToCheckForDeletion << it->second;
qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << it->second; qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << it->second;
_fileMappings.erase(it);
} else { } else {
qCDebug(asset_server) << "Unable to delete a mapping that was not found:" << path; qCDebug(asset_server) << "Unable to delete a mapping that was not found:" << path;
} }
@ -1165,10 +1164,11 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath,
void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector<QString> bakedFilePaths) { void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector<QString> bakedFilePaths) {
bool errorCompletingBake { false }; bool errorCompletingBake { false };
QString errorReason;
qDebug() << "Completing bake for " << originalAssetHash; qDebug() << "Completing bake for " << originalAssetHash;
for (auto& filePath: bakedFilePaths) { for (auto& filePath : bakedFilePaths) {
// figure out the hash for the contents of this file // figure out the hash for the contents of this file
QFile file(filePath); QFile file(filePath);
@ -1184,6 +1184,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
} else { } else {
// stop handling this bake, couldn't hash the contents of the file // stop handling this bake, couldn't hash the contents of the file
errorCompletingBake = true; errorCompletingBake = true;
errorReason = "Failed to finalize bake";
break; break;
} }
@ -1194,6 +1195,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) { if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) {
// stop handling this bake, couldn't copy the bake file into our files directory // stop handling this bake, couldn't copy the bake file into our files directory
errorCompletingBake = true; errorCompletingBake = true;
errorReason = "Failed to copy baked assets to asset server";
break; break;
} }
} }
@ -1220,12 +1222,14 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
qDebug() << "Failed to set mapping"; qDebug() << "Failed to set mapping";
// stop handling this bake, couldn't add a mapping for this bake file // stop handling this bake, couldn't add a mapping for this bake file
errorCompletingBake = true; errorCompletingBake = true;
errorReason = "Failed to finalize bake";
break; break;
} }
} else { } else {
qDebug() << "Failed to open baked file: " << filePath; qDebug() << "Failed to open baked file: " << filePath;
// stop handling this bake, we couldn't open one of the files for reading // stop handling this bake, we couldn't open one of the files for reading
errorCompletingBake = true; errorCompletingBake = true;
errorReason = "Failed to finalize bake";
break; break;
} }
} }
@ -1235,6 +1239,10 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
writeMetaFile(originalAssetHash); writeMetaFile(originalAssetHash);
} else { } else {
qWarning() << "Could not complete bake for" << originalAssetHash; qWarning() << "Could not complete bake for" << originalAssetHash;
AssetMeta meta;
meta.failedLastBake = true;
meta.lastBakeErrors = errorReason;
writeMetaFile(originalAssetHash, meta);
} }
_pendingBakes.remove(originalAssetHash); _pendingBakes.remove(originalAssetHash);
@ -1246,7 +1254,6 @@ void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath
} }
static const QString BAKE_VERSION_KEY = "bake_version"; static const QString BAKE_VERSION_KEY = "bake_version";
static const QString APP_VERSION_KEY = "app_version";
static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake"; static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake";
static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors"; static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors";
@ -1273,18 +1280,15 @@ std::pair<bool, AssetMeta> AssetServer::readMetaFile(AssetHash hash) {
auto root = doc.object(); auto root = doc.object();
auto bakeVersion = root[BAKE_VERSION_KEY].toInt(-1); auto bakeVersion = root[BAKE_VERSION_KEY].toInt(-1);
auto appVersion = root[APP_VERSION_KEY].toInt(-1);
auto failedLastBake = root[FAILED_LAST_BAKE_KEY]; auto failedLastBake = root[FAILED_LAST_BAKE_KEY];
auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY]; auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY];
if (bakeVersion != -1 if (bakeVersion != -1
&& appVersion != -1
&& failedLastBake.isBool() && failedLastBake.isBool()
&& lastBakeErrors.isString()) { && lastBakeErrors.isString()) {
AssetMeta meta; AssetMeta meta;
meta.bakeVersion = bakeVersion; meta.bakeVersion = bakeVersion;
meta.applicationVersion = appVersion;
meta.failedLastBake = failedLastBake.toBool(); meta.failedLastBake = failedLastBake.toBool();
meta.lastBakeErrors = lastBakeErrors.toString(); meta.lastBakeErrors = lastBakeErrors.toString();
@ -1303,7 +1307,6 @@ bool AssetServer::writeMetaFile(AssetHash originalAssetHash, const AssetMeta& me
QJsonObject metaFileObject; QJsonObject metaFileObject;
metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion; metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion;
metaFileObject[APP_VERSION_KEY] = meta.applicationVersion;
metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake;
metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors; metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors;

View file

@ -34,7 +34,6 @@ struct AssetMeta {
} }
int bakeVersion { 0 }; int bakeVersion { 0 };
int applicationVersion { 0 };
bool failedLastBake { false }; bool failedLastBake { false };
QString lastBakeErrors; QString lastBakeErrors;
}; };
@ -79,7 +78,7 @@ private:
bool setMapping(AssetPath path, AssetHash hash); bool setMapping(AssetPath path, AssetHash hash);
/// Delete mapping `path`. Returns `true` if deletion of mappings succeeds, else `false`. /// Delete mapping `path`. Returns `true` if deletion of mappings succeeds, else `false`.
bool deleteMappings(AssetPathList& paths); bool deleteMappings(const AssetPathList& paths);
/// Rename mapping from `oldPath` to `newPath`. Returns true if successful /// Rename mapping from `oldPath` to `newPath`. Returns true if successful
bool renameMapping(AssetPath oldPath, AssetPath newPath); bool renameMapping(AssetPath oldPath, AssetPath newPath);

View file

@ -49,9 +49,14 @@ ScrollingWindow {
Component.onCompleted: { Component.onCompleted: {
ApplicationInterface.uploadRequest.connect(uploadClicked); ApplicationInterface.uploadRequest.connect(uploadClicked);
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
assetMappingsModel.autoRefreshEnabled = true;
reload(); reload();
} }
Component.onDestruction: {
assetMappingsModel.autoRefreshEnabled = false;
}
function doDeleteFile(path) { function doDeleteFile(path) {
console.log("Deleting " + path); console.log("Deleting " + path);
@ -146,7 +151,6 @@ ScrollingWindow {
function reload() { function reload() {
Assets.mappingModel.refresh(); Assets.mappingModel.refresh();
treeView.selection.clear();
} }
function handleGetMappingsError(errorString) { function handleGetMappingsError(errorString) {
@ -502,16 +506,6 @@ ScrollingWindow {
onClicked: root.deleteFile() onClicked: root.deleteFile()
enabled: treeView.selection.hasSelection enabled: treeView.selection.hasSelection
} }
HifiControls.GlyphButton {
glyph: hifi.glyphs.reload
color: hifi.buttons.black
colorScheme: root.colorScheme
width: hifi.dimensions.controlLineHeight
onClicked: root.reload()
}
} }
} }
@ -751,7 +745,7 @@ ScrollingWindow {
var path = assetProxyModel.data(index, 0x100); var path = assetProxyModel.data(index, 0x100);
mappings.push(path); mappings.push(path);
} }
print("Setting baking enabled:" + mappings + checked); print("Setting baking enabled:" + mappings + " " + checked);
Assets.setBakingEnabled(mappings, checked, function() { Assets.setBakingEnabled(mappings, checked, function() {
reload(); reload();
}); });

View file

@ -49,9 +49,15 @@ Rectangle {
isHMD = HMD.active; isHMD = HMD.active;
ApplicationInterface.uploadRequest.connect(uploadClicked); ApplicationInterface.uploadRequest.connect(uploadClicked);
assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError);
assetMappingsModel.autoRefreshEnabled = true;
reload(); reload();
} }
Component.onDestruction: {
assetMappingsModel.autoRefreshEnabled = false;
}
function doDeleteFile(path) { function doDeleteFile(path) {
console.log("Deleting " + path); console.log("Deleting " + path);
@ -145,7 +151,6 @@ Rectangle {
function reload() { function reload() {
Assets.mappingModel.refresh(); Assets.mappingModel.refresh();
treeView.selection.clear();
} }
function handleGetMappingsError(errorString) { function handleGetMappingsError(errorString) {
@ -502,16 +507,6 @@ Rectangle {
onClicked: root.deleteFile() onClicked: root.deleteFile()
enabled: treeView.selection.hasSelection enabled: treeView.selection.hasSelection
} }
HifiControls.GlyphButton {
glyph: hifi.glyphs.reload
color: hifi.buttons.black
colorScheme: root.colorScheme
width: hifi.dimensions.controlLineHeight
onClicked: root.reload()
}
} }
} }
@ -748,7 +743,7 @@ Rectangle {
var path = assetProxyModel.data(index, 0x100); var path = assetProxyModel.data(index, 0x100);
mappings.push(path); mappings.push(path);
} }
print("Setting baking enabled:" + mappings + checked); print("Setting baking enabled:" + mappings + " " + checked);
Assets.setBakingEnabled(mappings, checked, function() { Assets.setBakingEnabled(mappings, checked, function() {
reload(); reload();
}); });

View file

@ -400,6 +400,17 @@ Rectangle {
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
TableViewColumn {
role: "display";
}
onActivated: {
var path = scriptsModel.data(index, 0x100)
if (path) {
loadScript(path)
}
}
} }
HifiControls.VerticalSpacer { HifiControls.VerticalSpacer {
@ -426,13 +437,6 @@ Rectangle {
} }
} }
} }
onActivated: {
var path = scriptsModel.data(index, 0x100)
if (path) {
loadScript(path)
}
}
} }
Item { Item {

View file

@ -2196,6 +2196,14 @@ float MyAvatar::getDomainMaxScale() {
return _domainMaximumScale; return _domainMaximumScale;
} }
void MyAvatar::setGravity(float gravity) {
_characterController.setGravity(gravity);
}
float MyAvatar::getGravity() {
return _characterController.getGravity();
}
void MyAvatar::increaseSize() { void MyAvatar::increaseSize() {
// make sure we're starting from an allowable scale // make sure we're starting from an allowable scale
clampTargetScaleToDomainLimits(); clampTargetScaleToDomainLimits();

View file

@ -152,7 +152,7 @@ class MyAvatar : public Avatar {
Q_PROPERTY(float userHeight READ getUserHeight WRITE setUserHeight) Q_PROPERTY(float userHeight READ getUserHeight WRITE setUserHeight)
Q_PROPERTY(float userEyeHeight READ getUserEyeHeight) Q_PROPERTY(float userEyeHeight READ getUserEyeHeight)
const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right"; const QString DOMINANT_RIGHT_HAND = "right";
@ -551,6 +551,9 @@ public slots:
float getDomainMinScale(); float getDomainMinScale();
float getDomainMaxScale(); float getDomainMaxScale();
void setGravity(float gravity);
float getGravity();
void goToLocation(const glm::vec3& newPosition, void goToLocation(const glm::vec3& newPosition,
bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(),
bool shouldFaceLocation = false); bool shouldFaceLocation = false);

View file

@ -19,8 +19,13 @@
#include <AssetUpload.h> #include <AssetUpload.h>
#include <MappingRequest.h> #include <MappingRequest.h>
#include <NetworkLogging.h> #include <NetworkLogging.h>
#include <NodeList.h>
#include <OffscreenUi.h> #include <OffscreenUi.h>
static const int AUTO_REFRESH_INTERVAL = 1000;
int assetMappingModelMetatypeId = qRegisterMetaType<AssetMappingModel*>("AssetMappingModel*");
AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() { AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() {
_proxyModel.setSourceModel(&_assetMappingModel); _proxyModel.setSourceModel(&_assetMappingModel);
_proxyModel.setSortRole(Qt::DisplayRole); _proxyModel.setSortRole(Qt::DisplayRole);
@ -189,6 +194,29 @@ void AssetMappingsScriptingInterface::setBakingEnabled(QStringList paths, bool e
AssetMappingModel::AssetMappingModel() { AssetMappingModel::AssetMappingModel() {
setupRoles(); setupRoles();
connect(&_autoRefreshTimer, &QTimer::timeout, this, [this] {
auto nodeList = DependencyManager::get<NodeList>();
auto assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (assetServer) {
refresh();
}
});
_autoRefreshTimer.setInterval(AUTO_REFRESH_INTERVAL);
}
bool AssetMappingModel::isAutoRefreshEnabled() {
return _autoRefreshTimer.isActive();
}
void AssetMappingModel::setAutoRefreshEnabled(bool enabled) {
if (enabled != _autoRefreshTimer.isActive()) {
if (enabled) {
_autoRefreshTimer.start();
} else {
_autoRefreshTimer.stop();
}
}
} }
bool AssetMappingModel::isKnownFolder(QString path) const { bool AssetMappingModel::isKnownFolder(QString path) const {
@ -205,10 +233,7 @@ bool AssetMappingModel::isKnownFolder(QString path) const {
return false; return false;
} }
int assetMappingModelMetatypeId = qRegisterMetaType<AssetMappingModel*>("AssetMappingModel*");
void AssetMappingModel::refresh() { void AssetMappingModel::refresh() {
qDebug() << "Refreshing asset mapping model";
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
auto request = assetClient->createGetAllMappingsRequest(); auto request = assetClient->createGetAllMappingsRequest();

View file

@ -25,11 +25,16 @@
class AssetMappingModel : public QStandardItemModel { class AssetMappingModel : public QStandardItemModel {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool autoRefreshEnabled READ isAutoRefreshEnabled WRITE setAutoRefreshEnabled)
public: public:
AssetMappingModel(); AssetMappingModel();
Q_INVOKABLE void refresh(); Q_INVOKABLE void refresh();
bool isAutoRefreshEnabled();
void setAutoRefreshEnabled(bool enabled);
bool isKnownMapping(QString path) const { return _pathToItemMap.contains(path); } bool isKnownMapping(QString path) const { return _pathToItemMap.contains(path); }
bool isKnownFolder(QString path) const; bool isKnownFolder(QString path) const;
@ -44,6 +49,7 @@ private:
void setupRoles(); void setupRoles();
QHash<QString, QStandardItem*> _pathToItemMap; QHash<QString, QStandardItem*> _pathToItemMap;
QTimer _autoRefreshTimer;
}; };
Q_DECLARE_METATYPE(AssetMappingModel*) Q_DECLARE_METATYPE(AssetMappingModel*)

View file

@ -15,6 +15,10 @@
#include <EntityTreeRenderer.h> #include <EntityTreeRenderer.h>
#include <NetworkingConstants.h> #include <NetworkingConstants.h>
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees
static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees
static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters
@ -38,11 +42,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
_entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_DIMENSIONS;
_entityPropertyFlags += PROP_REGISTRATION_POINT; _entityPropertyFlags += PROP_REGISTRATION_POINT;
// initially, set _enabled to match the switch. Later we enable/disable via the getter/setters
// if we are in edit or pal (for instance). Note this is temporary, as we expect to enable this all
// the time after getting edge highlighting, etc...
_enabled = _settingSwitch.get();
auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>().data(); auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>().data();
connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&)));
connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&))); connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&)));
@ -65,40 +64,36 @@ ContextOverlayInterface::ContextOverlayInterface() {
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged); connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged);
} }
static const uint32_t MOUSE_HW_ID = 0;
static const uint32_t LEFT_HAND_HW_ID = 1; static const uint32_t LEFT_HAND_HW_ID = 1;
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters
static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims
static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_OFFSET_DISTANCE = 0.1f;
static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_OFFSET_ANGLE = 5.0f;
static const float CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE = 20.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f;
static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f;
static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f;
static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f;
static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f;
void ContextOverlayInterface::setEnabled(bool enabled) { void ContextOverlayInterface::setEnabled(bool enabled) {
// only enable/disable if the setting in 'on'. If it is 'off', _enabled = enabled;
// make sure _enabled is always false.
if (_settingSwitch.get()) {
_enabled = enabled;
} else {
_enabled = false;
}
} }
bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) {
if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) {
if (contextOverlayFilterPassed(entityItemID)) { if (contextOverlayFilterPassed(entityItemID)) {
if (event.getID() == MOUSE_HW_ID) {
enableEntityHighlight(entityItemID);
}
qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID;
// Add all necessary variables to the stack // Add all necessary variables to the stack
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
glm::vec3 cameraPosition = qApp->getCamera().getPosition(); glm::vec3 cameraPosition = qApp->getCamera().getPosition();
float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition);
glm::vec3 entityDimensions = entityProperties.getDimensions(); glm::vec3 entityDimensions = entityProperties.getDimensions();
glm::vec3 entityPosition = entityProperties.getPosition(); glm::vec3 entityPosition = entityProperties.getPosition();
glm::vec3 contextOverlayPosition = entityProperties.getPosition(); glm::vec3 contextOverlayPosition = entityProperties.getPosition();
@ -131,27 +126,22 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
// If the camera is inside the box... // If the camera is inside the box...
// ...position the Context Overlay 1 meter in front of the camera. // ...position the Context Overlay 1 meter in front of the camera.
contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT);
contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition);
} else if (distanceFromCameraToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) {
// Else if the entity is too close to the camera...
// ...rotate the Context Overlay to the right of the entity.
// This makes it easy to inspect things you're holding.
float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE;
if (event.getID() == LEFT_HAND_HW_ID) {
offsetAngle *= -1;
}
contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition;
contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition);
} else { } else {
// Else, place the Context Overlay some offset away from the entity's bounding // Rotate the Context Overlay some number of degrees offset from the entity
// box in the direction of the camera. // along the line cast from your head to the entity's bounding box.
glm::vec3 direction = glm::normalize(entityPosition - cameraPosition); glm::vec3 direction = glm::normalize(entityPosition - cameraPosition);
float distance; float distance;
BoxFace face; BoxFace face;
glm::vec3 normal; glm::vec3 normal;
boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal);
contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE;
contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); if (event.getID() == LEFT_HAND_HW_ID) {
offsetAngle *= -1;
}
contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) *
((cameraPosition + direction * (distance - CONTEXT_OVERLAY_OFFSET_DISTANCE)));
contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition);
} }
// Finally, setup and draw the Context Overlay // Finally, setup and draw the Context Overlay
@ -176,6 +166,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
} }
} else { } else {
if (!_currentEntityWithContextOverlay.isNull()) { if (!_currentEntityWithContextOverlay.isNull()) {
disableEntityHighlight(_currentEntityWithContextOverlay);
return destroyContextOverlay(_currentEntityWithContextOverlay, event); return destroyContextOverlay(_currentEntityWithContextOverlay, event);
} }
return false; return false;
@ -237,13 +228,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID&
} }
void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) {
if (contextOverlayFilterPassed(entityID)) { if (contextOverlayFilterPassed(entityID) && _enabled && event.getID() != MOUSE_HW_ID) {
enableEntityHighlight(entityID); enableEntityHighlight(entityID);
} }
} }
void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) {
if (_currentEntityWithContextOverlay != entityID) { if (_currentEntityWithContextOverlay != entityID && _enabled && event.getID() != MOUSE_HW_ID) {
disableEntityHighlight(entityID); disableEntityHighlight(entityID);
} }
} }

View file

@ -76,8 +76,6 @@ private:
bool _isInMarketplaceInspectionMode { false }; bool _isInMarketplaceInspectionMode { false };
Setting::Handle<bool> _settingSwitch { "inspectionMode", false };
void openMarketplace(); void openMarketplace();
void enableEntityHighlight(const EntityItemID& entityItemID); void enableEntityHighlight(const EntityItemID& entityItemID);
void disableEntityHighlight(const EntityItemID& entityItemID); void disableEntityHighlight(const EntityItemID& entityItemID);

View file

@ -583,29 +583,17 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() }; QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() };
QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") };
if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) {
// re-baking an FBX that already references baked textures is a fail
// so we add an error and return from here
handleError("Cannot re-bake a file that references compressed textures");
return;
}
// make sure this texture points to something and isn't one we've already re-mapped // make sure this texture points to something and isn't one we've already re-mapped
if (!textureFileInfo.filePath().isEmpty()) { if (!textureFileInfo.filePath().isEmpty()) {
if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) {
// re-baking an FBX that already references baked textures is a fail
// so we add an error and return from here
handleError("Cannot re-bake a file that references compressed textures");
return;
}
// construct the new baked texture file name and file path
// ensuring that the baked texture will have a unique name
// even if there was another texture with the same name at a different path
auto bakedTextureFileName = createBakedTextureFileName(textureFileInfo);
QString bakedTextureFilePath {
_bakedOutputDir + "/" + bakedTextureFileName
};
_outputFiles.push_back(bakedTextureFilePath);
qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName
<< "to" << bakedTextureFileName;
// check if this was an embedded texture we have already have in-memory content for // check if this was an embedded texture we have already have in-memory content for
auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit());
@ -613,10 +601,29 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName, auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName,
!textureContent.isNull()); !textureContent.isNull());
QString bakedTextureFileName;
if (_remappedTexturePaths.contains(urlToTexture)) {
bakedTextureFileName = _remappedTexturePaths[urlToTexture];
} else {
// construct the new baked texture file name and file path
// ensuring that the baked texture will have a unique name
// even if there was another texture with the same name at a different path
bakedTextureFileName = createBakedTextureFileName(textureFileInfo);
_remappedTexturePaths[urlToTexture] = bakedTextureFileName;
}
qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName
<< "to" << bakedTextureFileName;
QString bakedTextureFilePath {
_bakedOutputDir + "/" + bakedTextureFileName
};
// write the new filename into the FBX scene // write the new filename into the FBX scene
textureChild.properties[0] = bakedTextureFileName.toLocal8Bit(); textureChild.properties[0] = bakedTextureFileName.toLocal8Bit();
if (!_bakingTextures.contains(urlToTexture)) { if (!_bakingTextures.contains(urlToTexture)) {
_outputFiles.push_back(bakedTextureFilePath);
// grab the ID for this texture so we can figure out the // grab the ID for this texture so we can figure out the
// texture type from the loaded materials // texture type from the loaded materials
@ -624,7 +631,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
auto textureType = textureTypes[textureID]; auto textureType = textureTypes[textureID];
// bake this texture asynchronously // bake this texture asynchronously
bakeTexture(urlToTexture, textureType, _bakedOutputDir, textureContent); bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent);
} }
} }
} }
@ -644,10 +651,10 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
} }
void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDir, const QByteArray& textureContent) { const QDir& outputDir, const QString& bakedFilename, const QByteArray& textureContent) {
// start a bake for this texture and add it to our list to keep track of // start a bake for this texture and add it to our list to keep track of
QSharedPointer<TextureBaker> bakingTexture { QSharedPointer<TextureBaker> bakingTexture {
new TextureBaker(textureURL, textureType, outputDir, textureContent), new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent),
&TextureBaker::deleteLater &TextureBaker::deleteLater
}; };

View file

@ -71,7 +71,7 @@ private:
QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false); QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false);
void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir, void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir,
const QByteArray& textureContent = QByteArray()); const QString& bakedFilename, const QByteArray& textureContent = QByteArray());
QUrl _fbxURL; QUrl _fbxURL;
@ -91,6 +91,7 @@ private:
QMultiHash<QUrl, QSharedPointer<TextureBaker>> _bakingTextures; QMultiHash<QUrl, QSharedPointer<TextureBaker>> _bakingTextures;
QHash<QString, int> _textureNameMatchCount; QHash<QString, int> _textureNameMatchCount;
QHash<QUrl, QString> _remappedTexturePaths;
TextureBakerThreadGetter _textureThreadGetter; TextureBakerThreadGetter _textureThreadGetter;

View file

@ -26,15 +26,19 @@
const QString BAKED_TEXTURE_EXT = ".ktx"; const QString BAKED_TEXTURE_EXT = ".ktx";
TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDirectory, const QByteArray& textureContent) : const QDir& outputDirectory, const QString& bakedFilename,
const QByteArray& textureContent) :
_textureURL(textureURL), _textureURL(textureURL),
_originalTexture(textureContent), _originalTexture(textureContent),
_textureType(textureType), _textureType(textureType),
_outputDirectory(outputDirectory) _outputDirectory(outputDirectory),
_bakedTextureFileName(bakedFilename)
{ {
// figure out the baked texture filename if (bakedFilename.isEmpty()) {
auto originalFilename = textureURL.fileName(); // figure out the baked texture filename
_bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; auto originalFilename = textureURL.fileName();
_bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT;
}
} }
void TextureBaker::bake() { void TextureBaker::bake() {

View file

@ -28,7 +28,8 @@ class TextureBaker : public Baker {
public: public:
TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDirectory, const QByteArray& textureContent = QByteArray()); const QDir& outputDirectory, const QString& bakedFilename = QString(),
const QByteArray& textureContent = QByteArray());
static const QStringList getSupportedFormats(); static const QStringList getSupportedFormats();

View file

@ -222,6 +222,16 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
_renderablesToUpdate.insert({ entityId, renderable }); _renderablesToUpdate.insert({ entityId, renderable });
} }
// NOTE: Looping over all the entity renderers is likely to be a bottleneck in the future
// Currently, this is necessary because the model entity loading logic requires constant polling
// This was working fine because the entity server used to send repeated updates as your view changed,
// but with the improved entity server logic (PR 11141), updateInScene (below) would not be triggered enough
for (const auto& entry : _entitiesInScene) {
const auto& renderable = entry.second;
if (renderable) {
renderable->update(scene, transaction);
}
}
if (!_renderablesToUpdate.empty()) { if (!_renderablesToUpdate.empty()) {
for (const auto& entry : _renderablesToUpdate) { for (const auto& entry : _renderablesToUpdate) {
const auto& renderable = entry.second; const auto& renderable = entry.second;

View file

@ -291,6 +291,18 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans
}); });
} }
void EntityRenderer::update(const ScenePointer& scene, Transaction& transaction) {
if (!isValidRenderItem()) {
return;
}
if (!needsUpdate()) {
return;
}
doUpdate(scene, transaction, _entity);
}
// //
// Internal methods // Internal methods
// //
@ -304,6 +316,11 @@ bool EntityRenderer::needsRenderUpdate() const {
return needsRenderUpdateFromEntity(_entity); return needsRenderUpdateFromEntity(_entity);
} }
// Returns true if the item needs to have update called
bool EntityRenderer::needsUpdate() const {
return needsUpdateFromEntity(_entity);
}
// Returns true if the item in question needs to have updateInScene called because of changes in the entity // Returns true if the item in question needs to have updateInScene called because of changes in the entity
bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity) const { bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity) const {
bool success = false; bool success = false;

View file

@ -49,6 +49,8 @@ public:
virtual bool addToScene(const ScenePointer& scene, Transaction& transaction) final; virtual bool addToScene(const ScenePointer& scene, Transaction& transaction) final;
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction); virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction);
virtual void update(const ScenePointer& scene, Transaction& transaction);
protected: protected:
virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); }
virtual void onAddToScene(const EntityItemPointer& entity); virtual void onAddToScene(const EntityItemPointer& entity);
@ -71,6 +73,12 @@ protected:
// Returns true if the item in question needs to have updateInScene called because of changes in the entity // Returns true if the item in question needs to have updateInScene called because of changes in the entity
virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const; virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const;
// Returns true if the item in question needs to have update called
virtual bool needsUpdate() const;
// Returns true if the item in question needs to have update called because of changes in the entity
virtual bool needsUpdateFromEntity(const EntityItemPointer& entity) const { return false; }
// Will be called on the main thread from updateInScene. This can be used to fetch things like // Will be called on the main thread from updateInScene. This can be used to fetch things like
// network textures or model geometry from resource caches // network textures or model geometry from resource caches
virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { } virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { }
@ -80,6 +88,8 @@ protected:
// data in this method if using multi-threaded rendering // data in this method if using multi-threaded rendering
virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity); virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity);
virtual void doUpdate(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { }
// Called by the `render` method after `needsRenderUpdate` // Called by the `render` method after `needsRenderUpdate`
virtual void doRender(RenderArgs* args) = 0; virtual void doRender(RenderArgs* args) = 0;
@ -148,6 +158,15 @@ protected:
onRemoveFromSceneTyped(_typedEntity); onRemoveFromSceneTyped(_typedEntity);
} }
using Parent::needsUpdateFromEntity;
// Returns true if the item in question needs to have update called because of changes in the entity
virtual bool needsUpdateFromEntity(const EntityItemPointer& entity) const override final {
if (Parent::needsUpdateFromEntity(entity)) {
return true;
}
return needsUpdateFromTypedEntity(_typedEntity);
}
using Parent::needsRenderUpdateFromEntity; using Parent::needsRenderUpdateFromEntity;
// Returns true if the item in question needs to have updateInScene called because of changes in the entity // Returns true if the item in question needs to have updateInScene called because of changes in the entity
virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const override final { virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const override final {
@ -162,6 +181,11 @@ protected:
doRenderUpdateSynchronousTyped(scene, transaction, _typedEntity); doRenderUpdateSynchronousTyped(scene, transaction, _typedEntity);
} }
virtual void doUpdate(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) override final {
Parent::doUpdate(scene, transaction, entity);
doUpdateTyped(scene, transaction, _typedEntity);
}
virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity) override final { virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity) override final {
Parent::doRenderUpdateAsynchronous(entity); Parent::doRenderUpdateAsynchronous(entity);
doRenderUpdateAsynchronousTyped(_typedEntity); doRenderUpdateAsynchronousTyped(_typedEntity);
@ -170,6 +194,8 @@ protected:
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return false; } virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return false; }
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { } virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { }
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { } virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { }
virtual bool needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return false; }
virtual void doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { }
virtual void onAddToSceneTyped(const TypedEntityPointer& entity) { } virtual void onAddToSceneTyped(const TypedEntityPointer& entity) { }
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) { } virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) { }

View file

@ -904,7 +904,7 @@ using namespace render;
using namespace render::entities; using namespace render::entities;
ItemKey ModelEntityRenderer::getKey() { ItemKey ModelEntityRenderer::getKey() {
return ItemKey::Builder::opaqueShape().withTypeMeta(); return ItemKey::Builder().withTypeMeta();
} }
uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
@ -1026,12 +1026,16 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
entity->copyAnimationJointDataToModel(); entity->copyAnimationJointDataToModel();
} }
bool ModelEntityRenderer::needsRenderUpdate() const { bool ModelEntityRenderer::needsUpdate() const {
ModelPointer model; ModelPointer model;
withReadLock([&] { withReadLock([&] {
model = _model; model = _model;
}); });
if (_modelJustLoaded) {
return true;
}
if (model) { if (model) {
if (_needsJointSimulation || _moving || _animating) { if (_needsJointSimulation || _moving || _animating) {
return true; return true;
@ -1057,10 +1061,10 @@ bool ModelEntityRenderer::needsRenderUpdate() const {
return true; return true;
} }
} }
return Parent::needsRenderUpdate(); return Parent::needsUpdate();
} }
bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { bool ModelEntityRenderer::needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
if (resultWithReadLock<bool>([&] { if (resultWithReadLock<bool>([&] {
if (entity->hasModel() != _hasModel) { if (entity->hasModel() != _hasModel) {
return true; return true;
@ -1122,7 +1126,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
return false; return false;
} }
void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { void ModelEntityRenderer::doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
if (_hasModel != entity->hasModel()) { if (_hasModel != entity->hasModel()) {
_hasModel = entity->hasModel(); _hasModel = entity->hasModel();
} }
@ -1148,9 +1152,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
return; return;
} }
_modelJustLoaded = false;
// Check for addition // Check for addition
if (_hasModel && !(bool)_model) { if (_hasModel && !(bool)_model) {
model = std::make_shared<Model>(nullptr, entity.get()); model = std::make_shared<Model>(nullptr, entity.get());
connect(model.get(), &Model::setURLFinished, this, &ModelEntityRenderer::handleModelLoaded);
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
model->init(); model->init();
entity->setModel(model); entity->setModel(model);
@ -1175,8 +1181,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it
auto extents = model->getMeshExtents(); auto extents = model->getMeshExtents();
properties.setDimensions(extents.maximum - extents.minimum); properties.setDimensions(extents.maximum - extents.minimum);
qCDebug(entitiesrenderer) << "Autoresizing" qCDebug(entitiesrenderer) << "Autoresizing"
<< (!entity->getName().isEmpty() ? entity->getName() : entity->getModelURL()) << (!entity->getName().isEmpty() ? entity->getName() : entity->getModelURL())
<< "from mesh extents"; << "from mesh extents";
QMetaObject::invokeMethod(DependencyManager::get<EntityScriptingInterface>().data(), "editEntity", QMetaObject::invokeMethod(DependencyManager::get<EntityScriptingInterface>().data(), "editEntity",
@ -1203,7 +1209,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
entity->updateModelBounds(); entity->updateModelBounds();
} }
if (model->isVisible() != _visible) { if (model->isVisible() != _visible) {
// FIXME: this seems like it could be optimized if we tracked our last known visible state in // FIXME: this seems like it could be optimized if we tracked our last known visible state in
// the renderable item. As it stands now the model checks it's visible/invisible state // the renderable item. As it stands now the model checks it's visible/invisible state
@ -1234,7 +1239,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
}); });
} }
if (_animating) { if (_animating) {
if (!jointsMapped()) { if (!jointsMapped()) {
mapJoints(entity, model->getJointNames()); mapJoints(entity, model->getJointNames());
@ -1243,6 +1247,12 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
} }
} }
void ModelEntityRenderer::handleModelLoaded(bool success) {
if (success) {
_modelJustLoaded = true;
}
}
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items
void ModelEntityRenderer::doRender(RenderArgs* args) { void ModelEntityRenderer::doRender(RenderArgs* args) {
PROFILE_RANGE(render_detail, "MetaModelRender"); PROFILE_RANGE(render_detail, "MetaModelRender");

View file

@ -138,10 +138,10 @@ protected:
virtual ItemKey getKey() override; virtual ItemKey getKey() override;
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual bool needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual bool needsRenderUpdate() const override; virtual bool needsUpdate() const override;
virtual void doRender(RenderArgs* args) override; virtual void doRender(RenderArgs* args) override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
private: private:
void animate(const TypedEntityPointer& entity); void animate(const TypedEntityPointer& entity);
@ -151,6 +151,7 @@ private:
// Transparency is handled in ModelMeshPartPayload // Transparency is handled in ModelMeshPartPayload
virtual bool isTransparent() const override { return false; } virtual bool isTransparent() const override { return false; }
bool _modelJustLoaded { false };
bool _hasModel { false }; bool _hasModel { false };
::ModelPointer _model; ::ModelPointer _model;
GeometryResource::Pointer _compoundShapeResource; GeometryResource::Pointer _compoundShapeResource;
@ -178,6 +179,9 @@ private:
bool _animating { false }; bool _animating { false };
uint64_t _lastAnimated { 0 }; uint64_t _lastAnimated { 0 };
float _currentFrame { 0 }; float _currentFrame { 0 };
private slots:
void handleModelLoaded(bool success);
}; };
} } // namespace } } // namespace

View file

@ -102,7 +102,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));
transformToTopLeft.setRotation(orientation); transformToTopLeft.setRotation(orientation);
} }
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
batch.setModelTransform(transformToTopLeft); batch.setModelTransform(transformToTopLeft);

View file

@ -140,6 +140,11 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
_webSurface->resize(QSize(windowSize.x, windowSize.y)); _webSurface->resize(QSize(windowSize.x, windowSize.y));
} }
void WebEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
Parent::doRenderUpdateAsynchronousTyped(entity);
_modelTransform.postScale(entity->getDimensions());
}
void WebEntityRenderer::doRender(RenderArgs* args) { void WebEntityRenderer::doRender(RenderArgs* args) {
withWriteLock([&] { withWriteLock([&] {
_lastRenderTime = usecTimestampNow(); _lastRenderTime = usecTimestampNow();

View file

@ -29,6 +29,7 @@ protected:
virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdate() const override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override; virtual void doRender(RenderArgs* args) override;
virtual bool isTransparent() const override; virtual bool isTransparent() const override;

View file

@ -464,7 +464,7 @@ void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) {
_resource = resource; _resource = resource;
if (_resource) { if (_resource) {
if (_resource->isLoaded()) { if (_resource->isLoaded()) {
_geometryRef = std::make_shared<Geometry>(*_resource); resourceFinished(true);
} else { } else {
startWatching(); startWatching();
} }

View file

@ -120,7 +120,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
_dynamicsWorld->addAction(this); _dynamicsWorld->addAction(this);
// restore gravity settings because adding an object to the world overwrites its gravity setting // restore gravity settings because adding an object to the world overwrites its gravity setting
_rigidBody->setGravity(_gravity * _currentUp); _rigidBody->setGravity(_currentGravity * _currentUp);
btCollisionShape* shape = _rigidBody->getCollisionShape(); btCollisionShape* shape = _rigidBody->getCollisionShape();
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape)); _ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape));
@ -302,7 +302,7 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar
// add minimum velocity to counteract gravity's displacement during one step // add minimum velocity to counteract gravity's displacement during one step
// Note: the 0.5 factor comes from the fact that we really want the // Note: the 0.5 factor comes from the fact that we really want the
// average velocity contribution from gravity during the step // average velocity contribution from gravity during the step
stepUpSpeed -= 0.5f * _gravity * timeToStep; // remember: _gravity is negative scalar stepUpSpeed -= 0.5f * _currentGravity * timeToStep; // remember: _gravity is negative scalar
btScalar vDotUp = velocity.dot(_currentUp); btScalar vDotUp = velocity.dot(_currentUp);
if (vDotUp < stepUpSpeed) { if (vDotUp < stepUpSpeed) {
@ -351,6 +351,28 @@ static const char* stateToStr(CharacterController::State state) {
} }
#endif // #ifdef DEBUG_STATE_CHANGE #endif // #ifdef DEBUG_STATE_CHANGE
void CharacterController::updateCurrentGravity() {
int16_t collisionGroup = computeCollisionGroup();
if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_currentGravity = 0.0f;
} else {
_currentGravity = _gravity;
}
if (_rigidBody) {
_rigidBody->setGravity(_currentGravity * _currentUp);
}
}
void CharacterController::setGravity(float gravity) {
_gravity = gravity;
updateCurrentGravity();
}
float CharacterController::getGravity() {
return _gravity;
}
#ifdef DEBUG_STATE_CHANGE #ifdef DEBUG_STATE_CHANGE
void CharacterController::setState(State desiredState, const char* reason) { void CharacterController::setState(State desiredState, const char* reason) {
#else #else
@ -365,19 +387,7 @@ void CharacterController::setState(State desiredState) {
qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason;
#endif #endif
_state = desiredState; _state = desiredState;
updateGravity(); updateCurrentGravity();
}
}
void CharacterController::updateGravity() {
int16_t collisionGroup = computeCollisionGroup();
if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
_gravity = 0.0f;
} else {
_gravity = DEFAULT_AVATAR_GRAVITY;
}
if (_rigidBody) {
_rigidBody->setGravity(_gravity * _currentUp);
} }
} }
@ -436,14 +446,14 @@ void CharacterController::handleChangedCollisionGroup() {
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
} }
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP;
updateGravity(); updateCurrentGravity();
} }
} }
void CharacterController::updateUpAxis(const glm::quat& rotation) { void CharacterController::updateUpAxis(const glm::quat& rotation) {
_currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS);
if (_rigidBody) { if (_rigidBody) {
_rigidBody->setGravity(_gravity * _currentUp); _rigidBody->setGravity(_currentGravity * _currentUp);
} }
} }

View file

@ -24,6 +24,7 @@
#include "BulletUtil.h" #include "BulletUtil.h"
#include "CharacterGhostObject.h" #include "CharacterGhostObject.h"
#include "AvatarConstants.h"
const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
@ -42,15 +43,18 @@ const btScalar MAX_CHARACTER_MOTOR_TIMESCALE = 60.0f; // one minute
const btScalar MIN_CHARACTER_MOTOR_TIMESCALE = 0.05f; const btScalar MIN_CHARACTER_MOTOR_TIMESCALE = 0.05f;
class CharacterController : public btCharacterControllerInterface { class CharacterController : public btCharacterControllerInterface {
public: public:
CharacterController(); CharacterController();
virtual ~CharacterController(); virtual ~CharacterController();
bool needsRemoval() const; bool needsRemoval() const;
bool needsAddition() const; bool needsAddition() const;
virtual void setDynamicsWorld(btDynamicsWorld* world); virtual void setDynamicsWorld(btDynamicsWorld* world);
btCollisionObject* getCollisionObject() { return _rigidBody; } btCollisionObject* getCollisionObject() { return _rigidBody; }
void setGravity(float gravity);
float getGravity();
virtual void updateShapeIfNecessary() = 0; virtual void updateShapeIfNecessary() = 0;
// overrides from btCharacterControllerInterface // overrides from btCharacterControllerInterface
@ -131,7 +135,7 @@ protected:
#endif #endif
virtual void updateMassProperties() = 0; virtual void updateMassProperties() = 0;
void updateGravity(); void updateCurrentGravity();
void updateUpAxis(const glm::quat& rotation); void updateUpAxis(const glm::quat& rotation);
bool checkForSupport(btCollisionWorld* collisionWorld); bool checkForSupport(btCollisionWorld* collisionWorld);
@ -184,7 +188,8 @@ protected:
bool _stepUpEnabled { true }; bool _stepUpEnabled { true };
bool _hasSupport; bool _hasSupport;
btScalar _gravity { 0.0f }; btScalar _currentGravity { 0.0f };
btScalar _gravity { DEFAULT_AVATAR_GRAVITY };
btScalar _followTime; btScalar _followTime;
btVector3 _followLinearDisplacement; btVector3 _followLinearDisplacement;

View file

@ -321,17 +321,25 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren
} }
ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) :
_meshIndex(_meshIndex), _meshIndex(meshIndex),
_shapeID(shapeIndex) { _shapeID(shapeIndex) {
assert(model && model->isLoaded()); assert(model && model->isLoaded());
_model = model; _model = model;
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
const Model::MeshState& state = model->getMeshState(_meshIndex);
updateMeshPart(modelMesh, partIndex); updateMeshPart(modelMesh, partIndex);
computeAdjustedLocalBound(state.clusterMatrices);
updateTransform(transform, offsetTransform); updateTransform(transform, offsetTransform);
Transform renderTransform = transform;
if (state.clusterMatrices.size() == 1) {
renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0]));
}
updateTransformForSkinnedMesh(renderTransform, transform, state.clusterBuffer);
initCache(); initCache();
} }

View file

@ -209,11 +209,6 @@ void Model::updateRenderItems() {
return; return;
} }
glm::vec3 scale = getScale();
if (_collisionGeometry) {
// _collisionGeometry is already scaled
scale = glm::vec3(1.0f);
}
_needsUpdateClusterMatrices = true; _needsUpdateClusterMatrices = true;
_renderItemsNeedUpdate = false; _renderItemsNeedUpdate = false;
@ -221,7 +216,7 @@ void Model::updateRenderItems() {
// the application will ensure only the last lambda is actually invoked. // the application will ensure only the last lambda is actually invoked.
void* key = (void*)this; void* key = (void*)this;
std::weak_ptr<Model> weakSelf = shared_from_this(); std::weak_ptr<Model> weakSelf = shared_from_this();
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() {
// do nothing, if the model has already been destroyed. // do nothing, if the model has already been destroyed.
auto self = weakSelf.lock(); auto self = weakSelf.lock();
@ -1219,6 +1214,7 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const {
} }
void Model::createRenderItemSet() { void Model::createRenderItemSet() {
updateClusterMatrices();
if (_collisionGeometry) { if (_collisionGeometry) {
if (_collisionRenderItems.empty()) { if (_collisionRenderItems.empty()) {
createCollisionRenderItemSet(); createCollisionRenderItemSet();
@ -1269,7 +1265,6 @@ void Model::createVisibleRenderItemSet() {
shapeID++; shapeID++;
} }
} }
computeMeshPartLocalBounds();
} }
void Model::createCollisionRenderItemSet() { void Model::createCollisionRenderItemSet() {

View file

@ -81,10 +81,10 @@ public:
float getColorB() const { return color.b; } float getColorB() const { return color.b; }
glm::vec3 color{ 1.f, 0.7f, 0.2f }; glm::vec3 color{ 1.f, 0.7f, 0.2f };
float width{ 3.f }; float width{ 2.0f };
float intensity{ 1.f }; float intensity{ 0.9f };
float fillOpacityUnoccluded{ 0.35f }; float fillOpacityUnoccluded{ 0.0f };
float fillOpacityOccluded{ 0.1f }; float fillOpacityOccluded{ 0.0f };
bool glow{ false }; bool glow{ false };
signals: signals:

View file

@ -9,7 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
layout(location = 0) in vec4 inColor; in vec4 _color;
in float distanceFromCenter;
out vec4 _fragColor; out vec4 _fragColor;
@ -17,10 +18,10 @@ void main(void) {
// The incoming value actually ranges from -1 to 1, so modify it // The incoming value actually ranges from -1 to 1, so modify it
// so that it goes from 0 -> 1 -> 0 with the solid alpha being at // so that it goes from 0 -> 1 -> 0 with the solid alpha being at
// the center of the line // the center of the line
float alpha = 1.0 - abs(inColor.a); float alpha = 1.0 - abs(distanceFromCenter);
// Convert from a linear alpha curve to a sharp peaked one // Convert from a linear alpha curve to a sharp peaked one
alpha = pow(alpha, 10); alpha = _color.a * pow(alpha, 10);
// Drop everything where the curve falls off to nearly nothing // Drop everything where the curve falls off to nearly nothing
if (alpha <= 0.05) { if (alpha <= 0.05) {
@ -28,6 +29,5 @@ void main(void) {
} }
// Emit the color // Emit the color
_fragColor = vec4(inColor.rgb, alpha); _fragColor = vec4(_color.rgb, alpha);
return;
} }

View file

@ -18,7 +18,9 @@ layout(std140) uniform lineData {
vec4 color; vec4 color;
}; };
layout(location = 0) out vec4 _color; out vec4 _color;
// the distance from the center in 'quad space'
out float distanceFromCenter;
void main(void) { void main(void) {
_color = color; _color = color;
@ -45,11 +47,10 @@ void main(void) {
// Add or subtract the orthogonal vector based on a different vertex ID // Add or subtract the orthogonal vector based on a different vertex ID
// calculation // calculation
if (gl_VertexID < 2) { if (gl_VertexID < 2) {
// Use the alpha channel to store the distance from the center in 'quad space' distanceFromCenter = -1.0;
_color.a = -1.0;
eye.xyz -= orthogonal; eye.xyz -= orthogonal;
} else { } else {
_color.a = 1.0; distanceFromCenter = 1.0;
eye.xyz += orthogonal; eye.xyz += orthogonal;
} }

View file

@ -75,7 +75,7 @@ void main(void) {
max(0, 1.0 - shininess / 128.0), max(0, 1.0 - shininess / 128.0),
DEFAULT_METALLIC, DEFAULT_METALLIC,
specular, specular,
specular); vec3(clamp(emissiveAmount, 0.0, 1.0)));
} else { } else {
packDeferredFragment( packDeferredFragment(
normal, normal,

View file

@ -54,9 +54,24 @@ float RecordingScriptingInterface::playerLength() const {
return _player->length(); return _player->length();
} }
void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback) {
_player->queueClip(clipLoader->getClip());
if (callback.isFunction()) {
QScriptValueList args { true, url };
callback.call(_scriptEngine->globalObject(), args);
}
}
void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) {
auto clipLoader = DependencyManager::get<recording::ClipCache>()->getClipLoader(url); auto clipLoader = DependencyManager::get<recording::ClipCache>()->getClipLoader(url);
if (clipLoader->isLoaded()) {
qCDebug(scriptengine) << "Recording already loaded from" << url;
playClip(clipLoader, url, callback);
return;
}
// hold a strong pointer to the loading clip so that it has a chance to load // hold a strong pointer to the loading clip so that it has a chance to load
_clipLoaders.insert(clipLoader); _clipLoaders.insert(clipLoader);
@ -69,12 +84,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue
if (auto clipLoader = weakClipLoader.toStrongRef()) { if (auto clipLoader = weakClipLoader.toStrongRef()) {
qCDebug(scriptengine) << "Loaded recording from" << url; qCDebug(scriptengine) << "Loaded recording from" << url;
_player->queueClip(clipLoader->getClip()); playClip(clipLoader, url, callback);
if (callback.isFunction()) {
QScriptValueList args { true, url };
callback.call(_scriptEngine->globalObject(), args);
}
// drop our strong pointer to this clip so it is cleaned up // drop our strong pointer to this clip so it is cleaned up
_clipLoaders.remove(clipLoader); _clipLoaders.remove(clipLoader);

View file

@ -88,6 +88,9 @@ protected:
QSharedPointer<BaseScriptEngine> _scriptEngine; QSharedPointer<BaseScriptEngine> _scriptEngine;
QSet<recording::NetworkClipLoaderPointer> _clipLoaders; QSet<recording::NetworkClipLoaderPointer> _clipLoaders;
private:
void playClip(recording::NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback);
}; };
#endif // hifi_RecordingScriptingInterface_h #endif // hifi_RecordingScriptingInterface_h

View file

@ -77,13 +77,13 @@ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEve
normal.setProperty("x", event._normal.x); normal.setProperty("x", event._normal.x);
normal.setProperty("y", event._normal.y); normal.setProperty("y", event._normal.y);
normal.setProperty("z", event._normal.z); normal.setProperty("z", event._normal.z);
obj.setProperty("pos3D", normal); obj.setProperty("normal", normal);
QScriptValue direction = engine->newObject(); QScriptValue direction = engine->newObject();
direction.setProperty("x", event._direction.x); direction.setProperty("x", event._direction.x);
direction.setProperty("y", event._direction.y); direction.setProperty("y", event._direction.y);
direction.setProperty("z", event._direction.z); direction.setProperty("z", event._direction.z);
obj.setProperty("pos3D", direction); obj.setProperty("direction", direction);
bool isPrimaryButton = false; bool isPrimaryButton = false;
bool isSecondaryButton = false; bool isSecondaryButton = false;

View file

@ -211,12 +211,12 @@ class UrlHandler : public QObject {
public: public:
Q_INVOKABLE bool canHandleUrl(const QString& url) { Q_INVOKABLE bool canHandleUrl(const QString& url) {
static auto handler = dynamic_cast<AbstractUriHandler*>(qApp); static auto handler = dynamic_cast<AbstractUriHandler*>(qApp);
return handler->canAcceptURL(url); return handler && handler->canAcceptURL(url);
} }
Q_INVOKABLE bool handleUrl(const QString& url) { Q_INVOKABLE bool handleUrl(const QString& url) {
static auto handler = dynamic_cast<AbstractUriHandler*>(qApp); static auto handler = dynamic_cast<AbstractUriHandler*>(qApp);
return handler->acceptURL(url); return handler && handler->acceptURL(url);
} }
}; };

View file

@ -61,7 +61,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info)
// During the period in which we have HFC commerce in the system, but not applied everywhere: // During the period in which we have HFC commerce in the system, but not applied everywhere:
const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" }; const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" };
static Setting::Handle<bool> _settingSwitch{ "inspectionMode", false }; static Setting::Handle<bool> _settingSwitch{ "commerce", false };
bool isMoney = _settingSwitch.get(); bool isMoney = _settingSwitch.get();
const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse); const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse);

View file

@ -0,0 +1,45 @@
//
// Gravity Script 1.0
// ************
//
// Created by Cain Kilgore on 9/14/2017
// Javascript for the Gravity Modifier Implementation to test
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
function menuParameters(menuNameSelection, menuItemNameSelection) {
Menu.addMenuItem({
menuName: menuNameSelection,
menuItemName: menuItemNameSelection,
isCheckable: false
});
}
function setupMenu() {
if (!Menu.menuExists("Gravity")) {
Menu.addMenu("Gravity");
for (var i = -5; i <= 5; i++) {
menuParameters("Gravity", i);
}
}
}
function menuItemEvent(menuItem) {
for (var i = -5; i <= 5; i++) {
if (menuItem == i) {
MyAvatar.setGravity(i);
}
}
}
function onScriptEnding() {
Menu.removeMenu("Gravity");
}
setupMenu();
Menu.menuItemEvent.connect(menuItemEvent);
Script.scriptEnding.connect(onScriptEnding);

View file

@ -131,7 +131,7 @@
var button; var button;
var buttonName = "WALLET"; var buttonName = "WALLET";
var tablet = null; var tablet = null;
var walletEnabled = Settings.getValue("inspectionMode", false); var walletEnabled = Settings.getValue("commerce", false);
function startup() { function startup() {
if (walletEnabled) { if (walletEnabled) {
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");

View file

@ -148,7 +148,9 @@ Script.include("/~/system/libraries/controllers.js");
if (mode === "full") { if (mode === "full") {
var fullEndToEdit = PICK_WITH_HAND_RAY ? this.fullEnd : fullEnd; var fullEndToEdit = PICK_WITH_HAND_RAY ? this.fullEnd : fullEnd;
fullEndToEdit.dimensions = dim; fullEndToEdit.dimensions = dim;
LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit}); LaserPointers.editRenderState(laserPointerID, mode, { path: fullPath, end: fullEndToEdit });
this.contextOverlayTimer = false;
this.destroyContextOverlay();
} else if (mode === "half") { } else if (mode === "half") {
var halfEndToEdit = PICK_WITH_HAND_RAY ? this.halfEnd : halfEnd; var halfEndToEdit = PICK_WITH_HAND_RAY ? this.halfEnd : halfEnd;
halfEndToEdit.dimensions = dim; halfEndToEdit.dimensions = dim;

View file

@ -44,10 +44,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
return (this.hand === RIGHT_HAND) ? leftNearParentingGrabEntity : rightNearParentingGrabEntity; return (this.hand === RIGHT_HAND) ? leftNearParentingGrabEntity : rightNearParentingGrabEntity;
}; };
this.otherHandIsParent = function(props) {
return this.getOtherModule().thisHandIsParent(props);
};
this.thisHandIsParent = function(props) { this.thisHandIsParent = function(props) {
if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) {
return false; return false;
@ -99,12 +95,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
// this should never happen, but if it does, don't set previous parent to be this hand. // this should never happen, but if it does, don't set previous parent to be this hand.
// this.previousParentID[targetProps.id] = NULL; // this.previousParentID[targetProps.id] = NULL;
// this.previousParentJointIndex[targetProps.id] = -1; // this.previousParentJointIndex[targetProps.id] = -1;
} else if (this.otherHandIsParent(targetProps)) {
// the other hand is parent. Steal the object and information
var otherModule = this.getOtherModule();
this.previousParentID[targetProps.id] = otherModule.previousParentID[targetProps.id];
this.previousParentJointIndex[targetProps.id] = otherModule.previousParentJointIndex[targetProps.id];
otherModule.endNearParentingGrabEntity();
} else { } else {
this.previousParentID[targetProps.id] = targetProps.parentID; this.previousParentID[targetProps.id] = targetProps.parentID;
this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex; this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex;

View file

@ -298,6 +298,9 @@ Script.include("/~/system/libraries/controllers.js");
var intersection = controllerData.rayPicks[this.hand]; var intersection = controllerData.rayPicks[this.hand];
var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY);
var triggerOff = (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE); var triggerOff = (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE);
if (triggerOff) {
this.deleteContextOverlay();
}
var grabbingOverlay = this.grabModuleWantsNearbyOverlay(controllerData); var grabbingOverlay = this.grabModuleWantsNearbyOverlay(controllerData);
return offOverlay || grabbingOverlay || triggerOff; return offOverlay || grabbingOverlay || triggerOff;
}; };
@ -308,7 +311,6 @@ Script.include("/~/system/libraries/controllers.js");
this.laserPressExit(); this.laserPressExit();
this.laserPressingTarget = false; this.laserPressingTarget = false;
} }
this.deleteContextOverlay();
this.relinquishTouchFocus(); this.relinquishTouchFocus();
this.reset(); this.reset();
this.updateLaserPointer(); this.updateLaserPointer();

View file

@ -1,4 +1,4 @@
// handControllerGrab.js // scaleAvatar.js
// //
// Created by Dante Ruiz on 9/11/17 // Created by Dante Ruiz on 9/11/17
// //
@ -80,4 +80,5 @@
dispatcherUtils.disableDispatcherModule("LeftScaleAvatar"); dispatcherUtils.disableDispatcherModule("LeftScaleAvatar");
dispatcherUtils.disableDispatcherModule("RightScaleAvatar"); dispatcherUtils.disableDispatcherModule("RightScaleAvatar");
}; };
Script.scriptEnding.connect(this.cleanup);
})(); })();

View file

@ -0,0 +1,106 @@
// scaleEntity.js
//
// Created by Dante Ruiz on 9/18/17
//
// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global Script, Vec3, MyAvatar, RIGHT_HAND */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
(function() {
var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
function ScaleEntity(hand) {
this.hand = hand;
this.grabbedThingID = false;
this.scalingStartDistance = false;
this.scalingStartDimensions = false;
this.parameters = dispatcherUtils.makeDispatcherModuleParameters(
120,
this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"],
[],
100
);
this.otherHand = function() {
return this.hand === dispatcherUtils.RIGHT_HAND ? dispatcherUtils.LEFT_HAND : dispatcherUtils.RIGHT_HAND;
};
this.otherModule = function() {
return this.hand === dispatcherUtils.RIGHT_HAND ? leftScaleEntity : rightScaleEntity;
};
this.bumperPressed = function(controllerData) {
return ( controllerData.secondaryValues[this.hand] > dispatcherUtils.BUMPER_ON_VALUE);
};
this.getTargetProps = function(controllerData) {
// nearbyEntityProperties is already sorted by length from controller
var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand];
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
for (var i = 0; i < nearbyEntityProperties.length; i++) {
var props = nearbyEntityProperties[i];
var handPosition = controllerData.controllerLocations[this.hand].position;
var distance = Vec3.distance(props.position, handPosition);
if (distance > dispatcherUtils.NEAR_GRAB_RADIUS * sensorScaleFactor) {
continue;
}
if ((dispatcherUtils.entityIsGrabbable(props) ||
dispatcherUtils.propsArePhysical(props)) && !props.locked) {
return props;
}
}
return null;
};
this.isReady = function(controllerData) {
var otherModule = this.otherModule();
if (this.bumperPressed(controllerData) && otherModule.bumperPressed(controllerData)) {
var thisHandTargetProps = this.getTargetProps(controllerData);
var otherHandTargetProps = otherModule.getTargetProps(controllerData);
if (thisHandTargetProps && otherHandTargetProps) {
if (thisHandTargetProps.id === otherHandTargetProps.id) {
this.grabbedThingID = thisHandTargetProps.id;
this.scalingStartDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position,
controllerData.controllerLocations[this.otherHand()].position));
this.scalingStartDimensions = thisHandTargetProps.dimensions;
return dispatcherUtils.makeRunningValues(true, [], []);
}
}
}
return dispatcherUtils.makeRunningValues(false, [], []);
};
this.run = function(controllerData) {
var otherModule = this.otherModule();
if (this.bumperPressed(controllerData) && otherModule.bumperPressed(controllerData)) {
if (this.hand === dispatcherUtils.RIGHT_HAND) {
var scalingCurrentDistance =
Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position,
controllerData.controllerLocations[this.otherHand()].position));
var currentRescale = scalingCurrentDistance / this.scalingStartDistance;
var newDimensions = Vec3.multiply(currentRescale, this.scalingStartDimensions);
Entities.editEntity(this.grabbedThingID, { dimensions: newDimensions });
}
return dispatcherUtils.makeRunningValues(true, [], []);
}
return dispatcherUtils.makeRunningValues(false, [], []);
};
}
var leftScaleEntity = new ScaleEntity(dispatcherUtils.LEFT_HAND);
var rightScaleEntity = new ScaleEntity(dispatcherUtils.RIGHT_HAND);
dispatcherUtils.enableDispatcherModule("LeftScaleEntity", leftScaleEntity);
dispatcherUtils.enableDispatcherModule("RightScaleEntity", rightScaleEntity);
this.cleanup = function() {
dispatcherUtils.disableDispatcherModule("LeftScaleEntity");
dispatcherUtils.disableDispatcherModule("RightScaleEntity");
};
Script.scriptEnding.connect(this.cleanup);
})();

View file

@ -29,7 +29,8 @@ var CONTOLLER_SCRIPTS = [
"controllerModules/disableOtherModule.js", "controllerModules/disableOtherModule.js",
"controllerModules/farTrigger.js", "controllerModules/farTrigger.js",
"controllerModules/teleport.js", "controllerModules/teleport.js",
"controllerModules/scaleAvatar.js" "controllerModules/scaleAvatar.js",
"controllerModules/scaleEntity.js"
]; ];
var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; var DEBUG_MENU_ITEM = "Debug defaultScripts.js";

View file

@ -450,7 +450,7 @@
var parsedJsonMessage = JSON.parse(message); var parsedJsonMessage = JSON.parse(message);
if (parsedJsonMessage.type === "marketplaces") { if (parsedJsonMessage.type === "marketplaces") {
if (parsedJsonMessage.action === "inspectionModeSetting") { if (parsedJsonMessage.action === "commerceSetting") {
confirmAllPurchases = !!parsedJsonMessage.data; confirmAllPurchases = !!parsedJsonMessage.data;
injectCode(); injectCode();
} }
@ -458,7 +458,7 @@
} }
}); });
// Request inspection mode setting // Request commerce setting
// Code is injected into the webpage after the setting comes back. // Code is injected into the webpage after the setting comes back.
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "REQUEST_SETTING" type: "REQUEST_SETTING"

View file

@ -319,7 +319,11 @@ if (typeof module !== 'undefined') {
LEFT_HAND: LEFT_HAND, LEFT_HAND: LEFT_HAND,
RIGHT_HAND: RIGHT_HAND, RIGHT_HAND: RIGHT_HAND,
BUMPER_ON_VALUE: BUMPER_ON_VALUE, BUMPER_ON_VALUE: BUMPER_ON_VALUE,
propsArePhysical: propsArePhysical,
entityIsGrabbable: entityIsGrabbable,
NEAR_GRAB_RADIUS: NEAR_GRAB_RADIUS,
projectOntoOverlayXYPlane: projectOntoOverlayXYPlane, projectOntoOverlayXYPlane: projectOntoOverlayXYPlane,
projectOntoEntityXYPlane: projectOntoEntityXYPlane projectOntoEntityXYPlane: projectOntoEntityXYPlane
}; };
} }

View file

@ -138,8 +138,8 @@
} else if (parsedJsonMessage.type === "REQUEST_SETTING") { } else if (parsedJsonMessage.type === "REQUEST_SETTING") {
tablet.emitScriptEvent(JSON.stringify({ tablet.emitScriptEvent(JSON.stringify({
type: "marketplaces", type: "marketplaces",
action: "inspectionModeSetting", action: "commerceSetting",
data: Settings.getValue("inspectionMode", false) data: Settings.getValue("commerce", false)
})); }));
} else if (parsedJsonMessage.type === "PURCHASES") { } else if (parsedJsonMessage.type === "PURCHASES") {
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);

View file

@ -12,7 +12,7 @@ setup_hifi_project(Quick Gui OpenGL)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries # link in the shared libraries
link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi) link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi ui)
package_libraries_for_deployment() package_libraries_for_deployment()

View file

@ -42,11 +42,15 @@
#include <LogHandler.h> #include <LogHandler.h>
#include <AssetClient.h> #include <AssetClient.h>
#include <gl/OffscreenGLCanvas.h>
#include <gpu/gl/GLBackend.h> #include <gpu/gl/GLBackend.h>
#include <gpu/gl/GLFramebuffer.h> #include <gpu/gl/GLFramebuffer.h>
#include <gpu/gl/GLTexture.h> #include <gpu/gl/GLTexture.h>
#include <gpu/StandardShaderLib.h> #include <gpu/StandardShaderLib.h>
#include <ui/OffscreenQmlSurface.h>
#include <AnimationCache.h> #include <AnimationCache.h>
#include <SimpleEntitySimulation.h> #include <SimpleEntitySimulation.h>
#include <EntityDynamicInterface.h> #include <EntityDynamicInterface.h>
@ -427,6 +431,10 @@ namespace render {
} }
} }
OffscreenGLCanvas* _chromiumShareContext{ nullptr };
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
// Create a simple OpenGL window that renders text in various ways // Create a simple OpenGL window that renders text in various ways
class QTestWindow : public QWindow, public AbstractViewStateInterface { class QTestWindow : public QWindow, public AbstractViewStateInterface {
@ -506,8 +514,6 @@ public:
AbstractViewStateInterface::setInstance(this); AbstractViewStateInterface::setInstance(this);
_octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr); _octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr);
_octree->init(); _octree->init();
// Prevent web entities from rendering
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory);
DependencyManager::set<ParentFinder>(_octree->getTree()); DependencyManager::set<ParentFinder>(_octree->getTree());
auto nodeList = DependencyManager::get<LimitedNodeList>(); auto nodeList = DependencyManager::get<LimitedNodeList>();
@ -535,6 +541,23 @@ public:
_renderThread.initialize(this, _initContext); _renderThread.initialize(this, _initContext);
_initContext.makeCurrent(); _initContext.makeCurrent();
if (nsightActive()) {
// Prevent web entities from rendering
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory);
} else {
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->setObjectName("ChromiumShareContext");
_chromiumShareContext->create(_initContext.qglContext());
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
// Make sure all QML surfaces share the main thread GL context
OffscreenQmlSurface::setSharedContext(_initContext.qglContext());
_initContext.makeCurrent();
}
// FIXME use a wait condition // FIXME use a wait condition
QThread::msleep(1000); QThread::msleep(1000);
_renderThread.submitFrame(gpu::FramePointer()); _renderThread.submitFrame(gpu::FramePointer());
@ -679,6 +702,7 @@ private:
_renderCount = _renderThread._presentCount.load(); _renderCount = _renderThread._presentCount.load();
update(); update();
_initContext.makeCurrent();
RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE, RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE,
0, RenderArgs::DEFAULT_RENDER_MODE, 0, RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);