mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 08:14:48 +02:00
Merge pull request #14094 from wayne-chen/interstitialMerged
Merged interstitial page/redirect domain improvements
This commit is contained in:
commit
91df342ae9
27 changed files with 824 additions and 907 deletions
BIN
interface/resources/images/interstitialPage/button.png
Normal file
BIN
interface/resources/images/interstitialPage/button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
BIN
interface/resources/images/interstitialPage/button_back.png
Normal file
BIN
interface/resources/images/interstitialPage/button_back.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
BIN
interface/resources/images/interstitialPage/button_hover.png
Normal file
BIN
interface/resources/images/interstitialPage/button_hover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
interface/resources/images/interstitialPage/button_tryAgain.png
Normal file
BIN
interface/resources/images/interstitialPage/button_tryAgain.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
File diff suppressed because it is too large
Load diff
|
@ -122,6 +122,7 @@
|
|||
#include <RecordingScriptingInterface.h>
|
||||
#include <UpdateSceneTask.h>
|
||||
#include <RenderViewTask.h>
|
||||
#include <render/EngineStats.h>
|
||||
#include <SecondaryCamera.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <ResourceRequest.h>
|
||||
|
@ -365,7 +366,6 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI
|
|||
|
||||
static const uint32_t INVALID_FRAME = UINT32_MAX;
|
||||
|
||||
static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation
|
||||
static const float INITIAL_QUERY_RADIUS = 10.0f; // priority radius for entities before physics enabled
|
||||
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
|
@ -5383,8 +5383,8 @@ void Application::resetPhysicsReadyInformation() {
|
|||
// collision information of nearby entities to make running bullet be safe.
|
||||
_fullSceneReceivedCounter = 0;
|
||||
_fullSceneCounterAtLastPhysicsCheck = 0;
|
||||
_nearbyEntitiesCountAtLastPhysicsCheck = 0;
|
||||
_nearbyEntitiesStabilityCount = 0;
|
||||
_gpuTextureMemSizeStabilityCount = 0;
|
||||
_gpuTextureMemSizeAtLastCheck = 0;
|
||||
_physicsEnabled = false;
|
||||
_octreeProcessor.startEntitySequence();
|
||||
}
|
||||
|
@ -5623,18 +5623,21 @@ void Application::update(float deltaTime) {
|
|||
// for nearby entities before starting bullet up.
|
||||
quint64 now = usecTimestampNow();
|
||||
if (isServerlessMode() || _octreeProcessor.isLoadSequenceComplete()) {
|
||||
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
|
||||
_lastPhysicsCheckTime = now;
|
||||
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
|
||||
_lastQueriedViews.clear(); // Force new view.
|
||||
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
if (gpuTextureMemSizeStable() || !enableInterstitial) {
|
||||
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
|
||||
_lastPhysicsCheckTime = now;
|
||||
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
|
||||
_lastQueriedViews.clear(); // Force new view.
|
||||
|
||||
// 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 (getMyAvatar()->isReadyForPhysics()) {
|
||||
_physicsEnabled = true;
|
||||
setIsInterstitialMode(false);
|
||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||
// 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 (getMyAvatar()->isReadyForPhysics()) {
|
||||
_physicsEnabled = true;
|
||||
setIsInterstitialMode(false);
|
||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (domainLoadingInProgress) {
|
||||
|
@ -6249,6 +6252,8 @@ int Application::sendNackPackets() {
|
|||
missingSequenceNumbers = sequenceNumberStats.getMissingSet();
|
||||
});
|
||||
|
||||
_isMissingSequenceNumbers = (missingSequenceNumbers.size() != 0);
|
||||
|
||||
// construct nack packet(s) for this node
|
||||
foreach(const OCTREE_PACKET_SEQUENCE& missingNumber, missingSequenceNumbers) {
|
||||
nackPacketList->writePrimitive(missingNumber);
|
||||
|
@ -6275,9 +6280,19 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
|
|||
const bool isModifiedQuery = !_physicsEnabled;
|
||||
if (isModifiedQuery) {
|
||||
// Create modified view that is a simple sphere.
|
||||
bool interstitialModeEnabled = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
|
||||
ConicalViewFrustum sphericalView;
|
||||
sphericalView.setSimpleRadius(INITIAL_QUERY_RADIUS);
|
||||
_octreeQuery.setConicalViews({ sphericalView });
|
||||
|
||||
if (interstitialModeEnabled) {
|
||||
ConicalViewFrustum farView;
|
||||
farView.set(_viewFrustum);
|
||||
_octreeQuery.setConicalViews({ sphericalView, farView });
|
||||
} else {
|
||||
_octreeQuery.setConicalViews({ sphericalView });
|
||||
}
|
||||
|
||||
_octreeQuery.setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE);
|
||||
static constexpr float MIN_LOD_ADJUST = -20.0f;
|
||||
_octreeQuery.setBoundaryLevelAdjust(MIN_LOD_ADJUST);
|
||||
|
@ -6589,69 +6604,23 @@ void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNode
|
|||
}
|
||||
}
|
||||
|
||||
bool Application::nearbyEntitiesAreReadyForPhysics() {
|
||||
// this is used to avoid the following scenario:
|
||||
// A table has some items sitting on top of it. The items are at rest, meaning they aren't active in bullet.
|
||||
// Someone logs in close to the table. They receive information about the items on the table before they
|
||||
// receive information about the table. The items are very close to the avatar's capsule, so they become
|
||||
// activated in bullet. This causes them to fall to the floor, because the table's shape isn't yet in bullet.
|
||||
EntityTreePointer entityTree = getEntities()->getTree();
|
||||
if (!entityTree) {
|
||||
return false;
|
||||
}
|
||||
bool Application::gpuTextureMemSizeStable() {
|
||||
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
|
||||
auto renderStats = renderConfig->getConfig<render::EngineStats>("Stats");
|
||||
|
||||
// We don't want to use EntityTree::findEntities(AABox, ...) method because that scan will snarf parented entities
|
||||
// whose bounding boxes cannot be computed (it is too loose for our purposes here). Instead we manufacture
|
||||
// custom filters and use the general-purpose EntityTree::findEntities(filter, ...)
|
||||
QVector<EntityItemPointer> entities;
|
||||
AABox avatarBox(getMyAvatar()->getWorldPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE));
|
||||
// create two functions that use avatarBox (entityScan and elementScan), the second calls the first
|
||||
std::function<bool (EntityItemPointer&)> entityScan = [=](EntityItemPointer& entity) {
|
||||
if (entity->shouldBePhysical()) {
|
||||
bool success = false;
|
||||
AABox entityBox = entity->getAABox(success);
|
||||
// important: bail for entities that cannot supply a valid AABox
|
||||
return success && avatarBox.touches(entityBox);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
std::function<bool(const OctreeElementPointer&, void*)> elementScan = [&](const OctreeElementPointer& element, void* unused) {
|
||||
if (element->getAACube().touches(avatarBox)) {
|
||||
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
|
||||
entityTreeElement->getEntities(entityScan, entities);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
qint64 textureResourceGPUMemSize = renderStats->textureResourceGPUMemSize;
|
||||
qint64 texturePopulatedGPUMemSize = renderStats->textureResourcePopulatedGPUMemSize;
|
||||
qint64 textureTransferSize = renderStats->texturePendingGPUTransferSize;
|
||||
|
||||
entityTree->withReadLock([&] {
|
||||
// Pass the second function to the general-purpose EntityTree::findEntities()
|
||||
// which will traverse the tree, apply the two filter functions (to element, then to entities)
|
||||
// as it traverses. The end result will be a list of entities that match.
|
||||
entityTree->findEntities(elementScan, entities);
|
||||
});
|
||||
|
||||
uint32_t nearbyCount = entities.size();
|
||||
if (nearbyCount == _nearbyEntitiesCountAtLastPhysicsCheck) {
|
||||
_nearbyEntitiesStabilityCount++;
|
||||
if (_gpuTextureMemSizeAtLastCheck == textureResourceGPUMemSize) {
|
||||
_gpuTextureMemSizeStabilityCount++;
|
||||
} else {
|
||||
_nearbyEntitiesStabilityCount = 0;
|
||||
_gpuTextureMemSizeStabilityCount = 0;
|
||||
}
|
||||
_nearbyEntitiesCountAtLastPhysicsCheck = nearbyCount;
|
||||
_gpuTextureMemSizeAtLastCheck = textureResourceGPUMemSize;
|
||||
|
||||
const uint32_t MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT = 3;
|
||||
if (_nearbyEntitiesStabilityCount >= MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT) {
|
||||
// We've seen the same number of nearby entities for several stats packets in a row. assume we've got all
|
||||
// the local entities.
|
||||
bool result = true;
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {
|
||||
HIFI_FCDEBUG(interfaceapp(), "Physics disabled until entity loads: " << entity->getID() << entity->getName());
|
||||
// don't break here because we want all the relevant entities to start their downloads
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
if (_gpuTextureMemSizeStabilityCount >= _minimumGPUTextureMemSizeStabilityCount) {
|
||||
return (textureResourceGPUMemSize == texturePopulatedGPUMemSize) && (textureTransferSize == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -183,6 +183,8 @@ public:
|
|||
// passes, mirror window passes, etc
|
||||
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
|
||||
|
||||
bool isMissingSequenceNumbers() { return _isMissingSequenceNumbers; }
|
||||
|
||||
const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; }
|
||||
|
||||
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||
|
@ -230,6 +232,8 @@ public:
|
|||
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
|
||||
void setSettingConstrainToolbarPosition(bool setting);
|
||||
|
||||
Q_INVOKABLE void setMinimumGPUTextureMemStabilityCount(int stabilityCount) { _minimumGPUTextureMemSizeStabilityCount = stabilityCount; }
|
||||
|
||||
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
|
||||
|
||||
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
|
||||
|
@ -525,7 +529,7 @@ private:
|
|||
bool importFromZIP(const QString& filePath);
|
||||
bool importImage(const QString& urlString);
|
||||
|
||||
bool nearbyEntitiesAreReadyForPhysics();
|
||||
bool gpuTextureMemSizeStable();
|
||||
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
|
||||
void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket);
|
||||
|
||||
|
@ -581,6 +585,8 @@ private:
|
|||
QElapsedTimer _lastTimeUpdated;
|
||||
QElapsedTimer _lastTimeRendered;
|
||||
|
||||
int _minimumGPUTextureMemSizeStabilityCount { 30 };
|
||||
|
||||
ShapeManager _shapeManager;
|
||||
PhysicalEntitySimulationPointer _entitySimulation;
|
||||
PhysicsEnginePointer _physicsEngine;
|
||||
|
@ -709,6 +715,8 @@ private:
|
|||
|
||||
bool _fakedMouseEvent { false };
|
||||
|
||||
bool _isMissingSequenceNumbers { false };
|
||||
|
||||
void checkChangeCursor();
|
||||
mutable QMutex _changeCursorLock { QMutex::Recursive };
|
||||
Qt::CursorShape _desiredCursor{ Qt::BlankCursor };
|
||||
|
@ -719,8 +727,10 @@ private:
|
|||
|
||||
std::atomic<uint32_t> _fullSceneReceivedCounter { 0 }; // how many times have we received a full-scene octree stats packet
|
||||
uint32_t _fullSceneCounterAtLastPhysicsCheck { 0 }; // _fullSceneReceivedCounter last time we checked physics ready
|
||||
uint32_t _nearbyEntitiesCountAtLastPhysicsCheck { 0 }; // how many in-range entities last time we checked physics ready
|
||||
uint32_t _nearbyEntitiesStabilityCount { 0 }; // how many times has _nearbyEntitiesCountAtLastPhysicsCheck been the same
|
||||
|
||||
qint64 _gpuTextureMemSizeStabilityCount { 0 };
|
||||
qint64 _gpuTextureMemSizeAtLastCheck { 0 };
|
||||
|
||||
quint64 _lastPhysicsCheckTime { usecTimestampNow() }; // when did we last check to see if physics was ready
|
||||
|
||||
bool _keyboardDeviceHasFocus { true };
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
//
|
||||
|
||||
#include "SafeLanding.h"
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "EntityTreeRenderer.h"
|
||||
#include "ModelEntityItem.h"
|
||||
#include "RenderableModelEntityItem.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "Application.h"
|
||||
|
||||
|
@ -39,6 +42,7 @@ void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityT
|
|||
_entityTree = entityTree;
|
||||
_trackedEntities.clear();
|
||||
_trackingEntities = true;
|
||||
_maxTrackedEntityCount = 0;
|
||||
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
|
||||
&EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity);
|
||||
connect(std::const_pointer_cast<EntityTree>(_entityTree).get(),
|
||||
|
@ -47,6 +51,7 @@ void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityT
|
|||
_sequenceNumbers.clear();
|
||||
_initialStart = INVALID_SEQUENCE;
|
||||
_initialEnd = INVALID_SEQUENCE;
|
||||
_startTime = usecTimestampNow();
|
||||
EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority);
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +60,7 @@ void SafeLanding::stopEntitySequence() {
|
|||
Locker lock(_lock);
|
||||
_trackingEntities = false;
|
||||
_maxTrackedEntityCount = 0;
|
||||
_trackedEntityStabilityCount = 0;
|
||||
_initialStart = INVALID_SEQUENCE;
|
||||
_initialEnd = INVALID_SEQUENCE;
|
||||
_trackedEntities.clear();
|
||||
|
@ -66,18 +72,17 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
|
|||
Locker lock(_lock);
|
||||
EntityItemPointer entity = _entityTree->findEntityByID(entityID);
|
||||
|
||||
if (entity) {
|
||||
if (entity && entity->getCreated() < _startTime) {
|
||||
|
||||
_trackedEntities.emplace(entityID, entity);
|
||||
int trackedEntityCount = (int)_trackedEntities.size();
|
||||
|
||||
if (trackedEntityCount > _maxTrackedEntityCount) {
|
||||
_maxTrackedEntityCount = trackedEntityCount;
|
||||
_trackedEntityStabilityCount = 0;
|
||||
}
|
||||
qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
|
||||
}
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Safe Landing: Null Entity: " << entityID;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,10 +109,10 @@ void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) {
|
|||
bool SafeLanding::isLoadSequenceComplete() {
|
||||
if (isEntityLoadingComplete() && isSequenceNumbersComplete()) {
|
||||
Locker lock(_lock);
|
||||
_trackedEntities.clear();
|
||||
_initialStart = INVALID_SEQUENCE;
|
||||
_initialEnd = INVALID_SEQUENCE;
|
||||
_entityTree = nullptr;
|
||||
_trackingEntities = false; // Don't track anything else that comes in.
|
||||
EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority);
|
||||
}
|
||||
|
||||
|
@ -116,11 +121,18 @@ bool SafeLanding::isLoadSequenceComplete() {
|
|||
|
||||
float SafeLanding::loadingProgressPercentage() {
|
||||
Locker lock(_lock);
|
||||
static const int MINIMUM_TRACKED_ENTITY_STABILITY_COUNT = 15;
|
||||
|
||||
float entityReadyPercentage = 0.0f;
|
||||
if (_maxTrackedEntityCount > 0) {
|
||||
return ((_maxTrackedEntityCount - _trackedEntities.size()) / (float)_maxTrackedEntityCount);
|
||||
entityReadyPercentage = ((_maxTrackedEntityCount - _trackedEntities.size()) / (float)_maxTrackedEntityCount);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
if (_trackedEntityStabilityCount < MINIMUM_TRACKED_ENTITY_STABILITY_COUNT) {
|
||||
entityReadyPercentage *= 0.20f;
|
||||
}
|
||||
|
||||
return entityReadyPercentage;
|
||||
}
|
||||
|
||||
bool SafeLanding::isSequenceNumbersComplete() {
|
||||
|
@ -130,11 +142,16 @@ bool SafeLanding::isSequenceNumbersComplete() {
|
|||
_initialEnd + SEQUENCE_MODULO - _initialStart;
|
||||
auto startIter = _sequenceNumbers.find(_initialStart);
|
||||
auto endIter = _sequenceNumbers.find(_initialEnd - 1);
|
||||
|
||||
bool missingSequenceNumbers = qApp->isMissingSequenceNumbers();
|
||||
if (sequenceSize == 0 ||
|
||||
(startIter != _sequenceNumbers.end()
|
||||
&& endIter != _sequenceNumbers.end()
|
||||
&& distance(startIter, endIter) == sequenceSize - 1) ) {
|
||||
_trackingEntities = false; // Don't track anything else that comes in.
|
||||
&& ((distance(startIter, endIter) == sequenceSize - 1) || !missingSequenceNumbers))) {
|
||||
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
if (!enableInterstitial) {
|
||||
_trackingEntities = false; // Don't track anything else that comes in.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -145,13 +162,13 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) {
|
|||
if (entity && !entity->getCollisionless()) {
|
||||
const auto& entityType = entity->getType();
|
||||
if (entityType == EntityTypes::Model) {
|
||||
ModelEntityItem * modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity).get();
|
||||
RenderableModelEntityItem * modelEntity = std::dynamic_pointer_cast<RenderableModelEntityItem>(entity).get();
|
||||
static const std::set<ShapeType> downloadedCollisionTypes
|
||||
{ SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL };
|
||||
bool hasAABox;
|
||||
entity->getAABox(hasAABox);
|
||||
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
|
||||
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape());
|
||||
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape() || modelEntity->computeShapeFailedToLoad());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,16 +183,20 @@ bool SafeLanding::isEntityLoadingComplete() {
|
|||
auto entityTree = qApp->getEntities();
|
||||
auto entityMapIter = _trackedEntities.begin();
|
||||
|
||||
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
|
||||
while (entityMapIter != _trackedEntities.end()) {
|
||||
auto entity = entityMapIter->second;
|
||||
|
||||
bool isVisuallyReady = true;
|
||||
|
||||
Settings settings;
|
||||
bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool();
|
||||
|
||||
if (enableInterstitial) {
|
||||
isVisuallyReady = (entity->isVisuallyReady() || !entityTree->renderableForEntityId(entityMapIter->first));
|
||||
auto entityRenderable = entityTree->renderableForEntityId(entityMapIter->first);
|
||||
if (!entityRenderable) {
|
||||
entityTree->addingEntity(entityMapIter->first);
|
||||
}
|
||||
|
||||
isVisuallyReady = entity->isVisuallyReady() || (!entityRenderable && !entity->isParentPathComplete());
|
||||
}
|
||||
|
||||
if (isEntityPhysicsReady(entity) && isVisuallyReady) {
|
||||
|
@ -188,6 +209,12 @@ bool SafeLanding::isEntityLoadingComplete() {
|
|||
entityMapIter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableInterstitial) {
|
||||
_trackedEntityStabilityCount++;
|
||||
}
|
||||
|
||||
|
||||
return _trackedEntities.empty();
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,9 @@ private:
|
|||
int _initialStart { INVALID_SEQUENCE };
|
||||
int _initialEnd { INVALID_SEQUENCE };
|
||||
int _maxTrackedEntityCount { 0 };
|
||||
int _trackedEntityStabilityCount { 0 };
|
||||
|
||||
quint64 _startTime { 0 };
|
||||
|
||||
struct SequenceLessThan {
|
||||
bool operator()(const int& a, const int& b) const;
|
||||
|
|
|
@ -39,6 +39,7 @@ WindowScriptingInterface::WindowScriptingInterface() {
|
|||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &WindowScriptingInterface::disconnectedFromDomain);
|
||||
|
||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
|
||||
connect(&domainHandler, &DomainHandler::redirectErrorStateChanged, this, &WindowScriptingInterface::redirectErrorStateChanged);
|
||||
|
||||
connect(qApp, &Application::svoImportRequested, [this](const QString& urlString) {
|
||||
static const QMetaMethod svoImportRequestedSignal =
|
||||
|
|
|
@ -611,6 +611,14 @@ signals:
|
|||
*/
|
||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when you try to visit a domain but are redirected into the error state.
|
||||
* @function Window.redirectErrorStateChanged
|
||||
* @param {boolean} isInErrorState - If <code>true</code>, the user has been redirected to the error URL.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void redirectErrorStateChanged(bool isInErrorState);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with
|
||||
* <code>includeAnimated = false</code> or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}.
|
||||
|
|
|
@ -310,10 +310,10 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
|||
}
|
||||
|
||||
auto entityID = entity->getEntityItemID();
|
||||
processedIds.insert(entityID);
|
||||
auto renderable = EntityRenderer::addToScene(*this, entity, scene, transaction);
|
||||
if (renderable) {
|
||||
_entitiesInScene.insert({ entityID, renderable });
|
||||
processedIds.insert(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -315,6 +315,14 @@ void RenderableModelEntityItem::getCollisionGeometryResource() {
|
|||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::computeShapeFailedToLoad() {
|
||||
if (!_compoundShapeResource) {
|
||||
getCollisionGeometryResource();
|
||||
}
|
||||
|
||||
return (_compoundShapeResource && _compoundShapeResource->isFailed());
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setShapeType(ShapeType type) {
|
||||
ModelEntityItem::setShapeType(type);
|
||||
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
|
||||
|
@ -343,7 +351,6 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
|||
|
||||
bool RenderableModelEntityItem::isReadyToComputeShape() const {
|
||||
ShapeType type = getShapeType();
|
||||
|
||||
auto model = getModel();
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
if (!model || getCompoundShapeURL().isEmpty()) {
|
||||
|
|
|
@ -81,6 +81,7 @@ public:
|
|||
|
||||
virtual bool isReadyToComputeShape() const override;
|
||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo) override;
|
||||
bool computeShapeFailedToLoad();
|
||||
|
||||
virtual bool contains(const glm::vec3& point) const override;
|
||||
void stopModelOverrideIfNoParent();
|
||||
|
|
|
@ -104,6 +104,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
_networkTexture.reset();
|
||||
});
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
entity->setVisuallyReady(true);
|
||||
});
|
||||
} else {
|
||||
bool textureNeedsUpdate = resultWithReadLock<bool>([&]{
|
||||
return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures);
|
||||
|
@ -113,6 +117,12 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
|
||||
});
|
||||
}
|
||||
|
||||
if (_networkTexture) {
|
||||
withWriteLock([&] {
|
||||
entity->setVisuallyReady(_networkTexture->isFailed() || _networkTexture->isLoaded());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
|
|
|
@ -166,6 +166,7 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte
|
|||
{
|
||||
_type = EntityTypes::ParticleEffect;
|
||||
setColor(DEFAULT_COLOR);
|
||||
_visuallyReady = false;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAlpha(float alpha) {
|
||||
|
@ -777,4 +778,4 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
|
|||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -551,6 +551,11 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu
|
|||
|
||||
graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture,
|
||||
image::TextureUsage::Type type, MapChannel channel) {
|
||||
|
||||
if (baseUrl.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto url = getTextureUrl(baseUrl, fbxTexture);
|
||||
const auto texture = DependencyManager::get<TextureCache>()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels);
|
||||
_textures[channel] = Texture { fbxTexture.name, texture };
|
||||
|
|
|
@ -161,8 +161,14 @@ void AddressManager::storeCurrentAddress() {
|
|||
// be loaded over http(s)
|
||||
// url.scheme() == URL_SCHEME_HTTP ||
|
||||
// url.scheme() == URL_SCHEME_HTTPS ||
|
||||
bool isInErrorState = DependencyManager::get<NodeList>()->getDomainHandler().isInErrorState();
|
||||
if (isConnected()) {
|
||||
currentAddressHandle.set(url);
|
||||
if (isInErrorState) {
|
||||
// save the last address visited before the problem url.
|
||||
currentAddressHandle.set(lastAddress());
|
||||
} else {
|
||||
currentAddressHandle.set(url);
|
||||
}
|
||||
} else {
|
||||
qCWarning(networking) << "Ignoring attempt to save current address because not connected to domain:" << url;
|
||||
}
|
||||
|
@ -861,6 +867,10 @@ void AddressManager::goToUser(const QString& username, bool shouldMatchOrientati
|
|||
QByteArray(), nullptr, requestParams);
|
||||
}
|
||||
|
||||
bool AddressManager::canGoBack() const {
|
||||
return (_backStack.size() > 0);
|
||||
}
|
||||
|
||||
void AddressManager::refreshPreviousLookup() {
|
||||
// if we have a non-empty previous lookup, fire it again now (but don't re-store it in the history)
|
||||
if (!_previousAPILookup.isEmpty()) {
|
||||
|
|
|
@ -254,6 +254,12 @@ public slots:
|
|||
*/
|
||||
void goToLastAddress() { handleUrl(_lastVisitedURL, LookupTrigger::AttemptedRefresh); }
|
||||
|
||||
/**jsdoc
|
||||
* Returns if going back is possible.
|
||||
* @function location.canGoBack
|
||||
*/
|
||||
bool canGoBack() const;
|
||||
|
||||
/**jsdoc
|
||||
* Refresh the current address, e.g., after connecting to a domain in order to position the user to the desired location.
|
||||
* @function location.refreshPreviousLookup
|
||||
|
|
|
@ -123,6 +123,7 @@ void DomainHandler::hardReset() {
|
|||
|
||||
softReset();
|
||||
_isInErrorState = false;
|
||||
emit redirectErrorStateChanged(_isInErrorState);
|
||||
|
||||
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
||||
_pendingDomainID = QUuid();
|
||||
|
@ -138,6 +139,11 @@ void DomainHandler::hardReset() {
|
|||
_pendingPath.clear();
|
||||
}
|
||||
|
||||
bool DomainHandler::isHardRefusal(int reasonCode) {
|
||||
return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch || reasonCode == (int)ConnectionRefusedReason::NotAuthorized ||
|
||||
reasonCode == (int)ConnectionRefusedReason::TimedOut);
|
||||
}
|
||||
|
||||
bool DomainHandler::getInterstitialModeEnabled() const {
|
||||
return _interstitialModeSettingLock.resultWithReadLock<bool>([&] {
|
||||
return _enableInterstitialMode.get();
|
||||
|
@ -327,6 +333,7 @@ void DomainHandler::setIsConnected(bool isConnected) {
|
|||
_isConnected = isConnected;
|
||||
|
||||
if (_isConnected) {
|
||||
_lastDomainConnectionError = -1;
|
||||
emit connectedToDomain(_domainURL);
|
||||
|
||||
if (_domainURL.scheme() == URL_SCHEME_HIFI && !_domainURL.host().isEmpty()) {
|
||||
|
@ -358,9 +365,11 @@ void DomainHandler::loadedErrorDomain(std::map<QString, QString> namedPaths) {
|
|||
|
||||
void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, int reasonCode, const QString& extraInfo) {
|
||||
_lastDomainConnectionError = reasonCode;
|
||||
if (getInterstitialModeEnabled()) {
|
||||
if (getInterstitialModeEnabled() && isHardRefusal(reasonCode)) {
|
||||
_errorDomainURL = errorUrl;
|
||||
_isInErrorState = true;
|
||||
qCDebug(networking) << "Error connecting to domain: " << reasonMessage;
|
||||
emit redirectErrorStateChanged(_isInErrorState);
|
||||
emit redirectToErrorDomainURL(_errorDomainURL);
|
||||
} else {
|
||||
emit domainConnectionRefused(reasonMessage, reasonCode, extraInfo);
|
||||
|
|
|
@ -187,8 +187,6 @@ private slots:
|
|||
signals:
|
||||
void domainURLChanged(QUrl domainURL);
|
||||
|
||||
void domainConnectionErrorChanged(int reasonCode);
|
||||
|
||||
// NOTE: the emission of completedSocketDiscovery does not mean a connection to DS is established
|
||||
// It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on
|
||||
void completedSocketDiscovery();
|
||||
|
@ -205,6 +203,7 @@ signals:
|
|||
|
||||
void domainConnectionRefused(QString reasonMessage, int reason, const QString& extraInfo);
|
||||
void redirectToErrorDomainURL(QUrl errorDomainURL);
|
||||
void redirectErrorStateChanged(bool isInErrorState);
|
||||
|
||||
void limitOfSilentDomainCheckInsReached();
|
||||
|
||||
|
@ -213,6 +212,8 @@ private:
|
|||
void sendDisconnectPacket();
|
||||
void hardReset();
|
||||
|
||||
bool isHardRefusal(int reasonCode);
|
||||
|
||||
QUuid _uuid;
|
||||
Node::LocalID _localID;
|
||||
QUrl _domainURL;
|
||||
|
@ -230,7 +231,7 @@ private:
|
|||
QString _pendingPath;
|
||||
QTimer _settingsTimer;
|
||||
mutable ReadWriteLockable _interstitialModeSettingLock;
|
||||
Setting::Handle<bool> _enableInterstitialMode{ "enableInterstitialMode", false };
|
||||
Setting::Handle<bool> _enableInterstitialMode{ "enableInterstitialMode", true };
|
||||
|
||||
QSet<QString> _domainConnectionRefusals;
|
||||
bool _hasCheckedForAccessToken { false };
|
||||
|
|
|
@ -39,8 +39,9 @@ var DEFAULT_SCRIPTS_SEPARATE = [
|
|||
//"system/chat.js"
|
||||
];
|
||||
|
||||
if (Settings.getValue("enableInterstitialMode", false)) {
|
||||
DEFAULT_SCRIPTS_SEPARATE.push("system/interstitialPage.js");
|
||||
if (Window.interstitialModeEnabled) {
|
||||
DEFAULT_SCRIPTS_COMBINED.push("system/interstitialPage.js");
|
||||
DEFAULT_SCRIPTS_COMBINED.push("system/redirectOverlays.js");
|
||||
}
|
||||
|
||||
// add a menu item for debugging
|
||||
|
|
|
@ -75,4 +75,4 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,8 +139,8 @@ function AppUi(properties) {
|
|||
that.isOpen = false;
|
||||
}
|
||||
}
|
||||
console.debug(that.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type +
|
||||
"\nNew screen URL: " + url + "\nCurrent app open status: " + that.isOpen + "\n");
|
||||
// console.debug(that.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type +
|
||||
// "\nNew screen URL: " + url + "\nCurrent app open status: " + that.isOpen + "\n");
|
||||
};
|
||||
|
||||
// Overwrite with the given properties:
|
||||
|
|
|
@ -110,6 +110,8 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
this.reticleMinY = MARGIN;
|
||||
this.reticleMaxY;
|
||||
|
||||
this.ignoredEntities = [];
|
||||
|
||||
var ACTION_TTL = 15; // seconds
|
||||
|
||||
var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object
|
||||
|
@ -314,6 +316,17 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
return point2d;
|
||||
};
|
||||
|
||||
this.restoreIgnoredEntities = function() {
|
||||
for (var i = 0; i < this.ignoredEntities; i++) {
|
||||
var data = {
|
||||
action: 'remove',
|
||||
id: this.ignoredEntities[i]
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
|
||||
}
|
||||
this.ignoredEntities = [];
|
||||
};
|
||||
|
||||
this.notPointingAtEntity = function(controllerData) {
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
var entityProperty = Entities.getEntityProperties(intersection.objectID);
|
||||
|
@ -323,6 +336,15 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
if ((intersection.type === Picks.INTERSECTED_ENTITY && entityType === "Web") ||
|
||||
intersection.type === Picks.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) {
|
||||
return true;
|
||||
} else if (intersection.type === Picks.INTERSECTED_ENTITY && !Window.isPhysicsEnabled()) {
|
||||
// add to ignored items.
|
||||
var data = {
|
||||
action: 'add',
|
||||
id: intersection.objectID
|
||||
};
|
||||
Messages.sendMessage('Hifi-Hand-RayPick-Blacklist', JSON.stringify(data));
|
||||
this.ignoredEntities.push(intersection.objectID);
|
||||
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -383,6 +405,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
this.isReady = function (controllerData) {
|
||||
if (HMD.active) {
|
||||
if (this.notPointingAtEntity(controllerData)) {
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
|
@ -394,9 +417,11 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
this.destroyContextOverlay();
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
};
|
||||
|
||||
|
@ -407,6 +432,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity",
|
||||
this.highlightedEntity);
|
||||
this.highlightedEntity = null;
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
this.intersectionDistance = controllerData.rayPicks[this.hand].distance;
|
||||
|
@ -437,6 +463,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.grabbedThingID
|
||||
|| HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) {
|
||||
this.endFarGrabAction();
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
|
@ -448,6 +475,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
for (var j = 0; j < nearGrabReadiness.length; j++) {
|
||||
if (nearGrabReadiness[j].active) {
|
||||
this.endFarGrabAction();
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
|
@ -466,6 +494,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
]);
|
||||
if (targetProps.href !== "") {
|
||||
AddressManager.handleLookupString(targetProps.href);
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
|
@ -583,6 +612,7 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity",
|
||||
this.highlightedEntity);
|
||||
this.highlightedEntity = null;
|
||||
this.restoreIgnoredEntities();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
var tablet = null;
|
||||
var button = null;
|
||||
|
||||
var errorConnectingToDomain = false;
|
||||
|
||||
// Tips have a character limit of 69
|
||||
var userTips = [
|
||||
"Tip: Visit TheSpot to explore featured domains!",
|
||||
|
@ -48,7 +50,7 @@
|
|||
"Tip: Use the Create app to import models and create custom entities.",
|
||||
"Tip: We're open source! Feel free to contribute to our code on GitHub!",
|
||||
"Tip: What emotes have you used in the Emote app?",
|
||||
"Tip: Take and share your snapshots with the everyone using the Snap app.",
|
||||
"Tip: Take and share your snapshots with everyone using the Snap app.",
|
||||
"Tip: Did you know you can show websites in-world by creating a web entity?",
|
||||
"Tip: Find out more information about domains by visiting our website!",
|
||||
"Tip: Did you know you can get cool new apps from the Marketplace?",
|
||||
|
@ -125,7 +127,7 @@
|
|||
localPosition: { x: 0.0 , y: -1.6, z: 0.0 },
|
||||
text: toolTip,
|
||||
textAlpha: 1,
|
||||
backgroundAlpha: 1,
|
||||
backgroundAlpha: 0.00393,
|
||||
lineHeight: 0.13,
|
||||
visible: isVisible,
|
||||
ignoreRayIntersection: true,
|
||||
|
@ -135,17 +137,47 @@
|
|||
parentID: anchorOverlay
|
||||
});
|
||||
|
||||
var loadingToTheSpotID = Overlays.addOverlay("image3d", {
|
||||
var loadingToTheSpotText = Overlays.addOverlay("text3d", {
|
||||
name: "Loading-Destination-Card-Text",
|
||||
localPosition: { x: 0.0 , y: -1.5, z: -0.3 },
|
||||
url: Script.resourcesPath() + "images/interstitialPage/goTo_button.png",
|
||||
localPosition: { x: 0.0 , y: -1.687, z: -0.3 },
|
||||
text: "Go To TheSpot",
|
||||
textAlpha: 1,
|
||||
backgroundAlpha: 0.00393,
|
||||
lineHeight: 0.10,
|
||||
visible: isVisible,
|
||||
ignoreRayIntersection: true,
|
||||
dimensions: {x: 1, y: 0.17},
|
||||
drawInFront: true,
|
||||
grabbable: false,
|
||||
localOrientation: Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }),
|
||||
parentID: anchorOverlay
|
||||
});
|
||||
|
||||
var loadingToTheSpotID = Overlays.addOverlay("image3d", {
|
||||
name: "Loading-Destination-Card-GoTo-Image",
|
||||
localPosition: { x: 0.0 , y: -1.75, z: -0.3 },
|
||||
url: Script.resourcesPath() + "images/interstitialPage/button.png",
|
||||
alpha: 1,
|
||||
visible: isVisible,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: false,
|
||||
drawInFront: true,
|
||||
grabbable: false,
|
||||
localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 180.0, z: 0.0 }),
|
||||
localOrientation: Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }),
|
||||
parentID: anchorOverlay
|
||||
});
|
||||
|
||||
var loadingToTheSpotHoverID = Overlays.addOverlay("image3d", {
|
||||
name: "Loading-Destination-Card-GoTo-Image-Hover",
|
||||
localPosition: { x: 0.0 , y: -1.75, z: -0.3 },
|
||||
url: Script.resourcesPath() + "images/interstitialPage/button_hover.png",
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: false,
|
||||
drawInFront: true,
|
||||
grabbable: false,
|
||||
localOrientation: Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }),
|
||||
parentID: anchorOverlay
|
||||
});
|
||||
|
||||
|
@ -185,10 +217,11 @@
|
|||
var currentDomain = "no domain";
|
||||
var timer = null;
|
||||
var target = 0;
|
||||
var textureMemSizeStabilityCount = 0;
|
||||
var textureMemSizeAtLastCheck = 0;
|
||||
|
||||
var connectionToDomainFailed = false;
|
||||
|
||||
|
||||
function getAnchorLocalYOffset() {
|
||||
var loadingSpherePosition = Overlays.getProperty(loadingSphereID, "position");
|
||||
var loadingSphereOrientation = Overlays.getProperty(loadingSphereID, "rotation");
|
||||
|
@ -227,6 +260,8 @@
|
|||
updateOverlays(false);
|
||||
startAudio();
|
||||
target = 0;
|
||||
textureMemSizeStabilityCount = 0;
|
||||
textureMemSizeAtLastCheck = 0;
|
||||
currentProgress = 0.1;
|
||||
connectionToDomainFailed = false;
|
||||
previousCameraMode = Camera.mode;
|
||||
|
@ -235,6 +270,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
function toggleInterstitialPage(isInErrorState) {
|
||||
errorConnectingToDomain = isInErrorState;
|
||||
if (!errorConnectingToDomain) {
|
||||
domainChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
function startAudio() {
|
||||
sample = Audio.playSound(tune, {
|
||||
localOnly: true,
|
||||
|
@ -251,11 +293,12 @@
|
|||
function domainChanged(domain) {
|
||||
if (domain !== currentDomain) {
|
||||
MyAvatar.restoreAnimation();
|
||||
resetValues();
|
||||
var name = location.placename;
|
||||
domainName = name.charAt(0).toUpperCase() + name.slice(1);
|
||||
var doRequest = true;
|
||||
if (name.length === 0 && location.href === "file:///~/serverless/tutorial.json") {
|
||||
domainName = "Serveless Domain (Tutorial)";
|
||||
domainName = "Serverless Domain (Tutorial)";
|
||||
doRequest = false;
|
||||
}
|
||||
var domainNameLeftMargin = getLeftMargin(domainNameTextID, domainName);
|
||||
|
@ -306,8 +349,30 @@
|
|||
|
||||
var THE_PLACE = (HifiAbout.buildVersion === "dev") ? "hifi://TheSpot-dev": "hifi://TheSpot";
|
||||
function clickedOnOverlay(overlayID, event) {
|
||||
if (loadingToTheSpotID === overlayID) {
|
||||
if (loadingToTheSpotHoverID === overlayID) {
|
||||
location.handleLookupString(THE_PLACE);
|
||||
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
|
||||
Overlays.editOverlay(loadingToTheSpotID, {visible: true});
|
||||
}
|
||||
}
|
||||
|
||||
function onEnterOverlay(overlayID, event) {
|
||||
if (currentDomain === "no domain") {
|
||||
return;
|
||||
}
|
||||
if (overlayID === loadingToTheSpotID) {
|
||||
Overlays.editOverlay(loadingToTheSpotID, {visible: false});
|
||||
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: true});
|
||||
}
|
||||
}
|
||||
|
||||
function onLeaveOverlay(overlayID, event) {
|
||||
if (currentDomain === "no domain") {
|
||||
return;
|
||||
}
|
||||
if (overlayID === loadingToTheSpotHoverID) {
|
||||
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
|
||||
Overlays.editOverlay(loadingToTheSpotID, {visible: true});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,6 +405,7 @@
|
|||
renderViewTask.getConfig("LightingModel")["enableDirectionalLight"] = physicsEnabled;
|
||||
renderViewTask.getConfig("LightingModel")["enablePointLight"] = physicsEnabled;
|
||||
Overlays.editOverlay(loadingSphereID, mainSphereProperties);
|
||||
Overlays.editOverlay(loadingToTheSpotText, properties);
|
||||
Overlays.editOverlay(loadingToTheSpotID, properties);
|
||||
Overlays.editOverlay(domainNameTextID, properties);
|
||||
Overlays.editOverlay(domainDescription, domainTextProperties);
|
||||
|
@ -347,10 +413,11 @@
|
|||
Overlays.editOverlay(loadingBarPlacard, properties);
|
||||
Overlays.editOverlay(loadingBarProgress, loadingBarProperties);
|
||||
|
||||
|
||||
Menu.setIsOptionChecked("Show Overlays", physicsEnabled);
|
||||
if (!HMD.active) {
|
||||
toolbar.writeProperty("visible", physicsEnabled);
|
||||
if (!DEBUG) {
|
||||
Menu.setIsOptionChecked("Show Overlays", physicsEnabled);
|
||||
if (!HMD.active) {
|
||||
toolbar.writeProperty("visible", physicsEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
resetValues();
|
||||
|
@ -360,7 +427,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function scaleInterstitialPage(sensorToWorldScale) {
|
||||
var yOffset = getAnchorLocalYOffset();
|
||||
var localPosition = {
|
||||
|
@ -372,19 +438,53 @@
|
|||
Overlays.editOverlay(anchorOverlay, { localPosition: localPosition });
|
||||
}
|
||||
|
||||
function sleep(milliseconds) {
|
||||
var start = new Date().getTime();
|
||||
for (var i = 0; i < 1e7; i++) {
|
||||
if ((new Date().getTime() - start) > milliseconds){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
var renderStats = Render.getConfig("Stats");
|
||||
var physicsEnabled = Window.isPhysicsEnabled();
|
||||
var thisInterval = Date.now();
|
||||
var deltaTime = (thisInterval - lastInterval);
|
||||
lastInterval = thisInterval;
|
||||
|
||||
var domainLoadingProgressPercentage = Window.domainLoadingProgress();
|
||||
|
||||
var progress = MIN_LOADING_PROGRESS * domainLoadingProgressPercentage;
|
||||
var progress = ((TOTAL_LOADING_PROGRESS * 0.4) * domainLoadingProgressPercentage);
|
||||
if (progress >= target) {
|
||||
target = progress;
|
||||
}
|
||||
|
||||
if (currentProgress >= (TOTAL_LOADING_PROGRESS * 0.4)) {
|
||||
var textureResourceGPUMemSize = renderStats.textureResourceGPUMemSize;
|
||||
var texturePopulatedGPUMemSize = renderStats.textureResourcePopulatedGPUMemSize;
|
||||
|
||||
if (textureMemSizeAtLastCheck === textureResourceGPUMemSize) {
|
||||
textureMemSizeStabilityCount++;
|
||||
} else {
|
||||
textureMemSizeStabilityCount = 0;
|
||||
}
|
||||
|
||||
textureMemSizeAtLastCheck = textureResourceGPUMemSize;
|
||||
|
||||
if (textureMemSizeStabilityCount >= 20) {
|
||||
|
||||
if (textureResourceGPUMemSize > 0) {
|
||||
// print((texturePopulatedGPUMemSize / textureResourceGPUMemSize));
|
||||
var gpuPercantage = (TOTAL_LOADING_PROGRESS * 0.6) * (texturePopulatedGPUMemSize / textureResourceGPUMemSize);
|
||||
var totalProgress = progress + gpuPercantage;
|
||||
if (totalProgress >= target) {
|
||||
target = totalProgress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((physicsEnabled && (currentProgress < TOTAL_LOADING_PROGRESS))) {
|
||||
target = TOTAL_LOADING_PROGRESS;
|
||||
}
|
||||
|
@ -399,30 +499,40 @@
|
|||
};
|
||||
|
||||
Overlays.editOverlay(loadingBarProgress, properties);
|
||||
if ((physicsEnabled && (currentProgress >= (TOTAL_LOADING_PROGRESS - EPSILON)))) {
|
||||
|
||||
if (errorConnectingToDomain) {
|
||||
updateOverlays(errorConnectingToDomain);
|
||||
// setting hover id to invisible
|
||||
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
|
||||
endAudio();
|
||||
currentDomain = "no domain";
|
||||
timer = null;
|
||||
// The toolbar doesn't become visible in time to match the speed of
|
||||
// the signal handling of redirectErrorStateChanged in both this script
|
||||
// and the redirectOverlays.js script. Use a sleep function to ensure
|
||||
// the toolbar becomes visible again.
|
||||
sleep(300);
|
||||
if (!HMD.active) {
|
||||
toolbar.writeProperty("visible", true);
|
||||
}
|
||||
return;
|
||||
} else if ((physicsEnabled && (currentProgress >= (TOTAL_LOADING_PROGRESS - EPSILON)))) {
|
||||
updateOverlays((physicsEnabled || connectionToDomainFailed));
|
||||
// setting hover id to invisible
|
||||
Overlays.editOverlay(loadingToTheSpotHoverID, {visible: false});
|
||||
endAudio();
|
||||
currentDomain = "no domain";
|
||||
timer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
timer = Script.setTimeout(update, BASIC_TIMER_INTERVAL_MS);
|
||||
}
|
||||
var whiteColor = {red: 255, green: 255, blue: 255};
|
||||
var greyColor = {red: 125, green: 125, blue: 125};
|
||||
Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay);
|
||||
Overlays.hoverEnterOverlay.connect(function(overlayID, event) {
|
||||
if (overlayID === loadingToTheSpotID) {
|
||||
Overlays.editOverlay(loadingToTheSpotID, { color: greyColor });
|
||||
}
|
||||
});
|
||||
Overlays.hoverEnterOverlay.connect(onEnterOverlay);
|
||||
|
||||
Overlays.hoverLeaveOverlay.connect(function(overlayID, event) {
|
||||
if (overlayID === loadingToTheSpotID) {
|
||||
Overlays.editOverlay(loadingToTheSpotID, { color: whiteColor });
|
||||
}
|
||||
});
|
||||
Overlays.hoverLeaveOverlay.connect(onLeaveOverlay);
|
||||
|
||||
location.hostChanged.connect(domainChanged);
|
||||
location.lookupResultsFinished.connect(function() {
|
||||
|
@ -430,6 +540,7 @@
|
|||
connectionToDomainFailed = !location.isConnected;
|
||||
}, 1200);
|
||||
});
|
||||
Window.redirectErrorStateChanged.connect(toggleInterstitialPage);
|
||||
|
||||
MyAvatar.sensorToWorldScaleChanged.connect(scaleInterstitialPage);
|
||||
MyAvatar.sessionUUIDChanged.connect(function() {
|
||||
|
@ -448,9 +559,17 @@
|
|||
});
|
||||
}
|
||||
|
||||
// set left margin of text.
|
||||
var loadingTextProperties = {
|
||||
leftMargin: getLeftMargin(loadingToTheSpotText, "Go To TheSpot") + 0.045
|
||||
};
|
||||
|
||||
Overlays.editOverlay(loadingToTheSpotText, loadingTextProperties);
|
||||
|
||||
function cleanup() {
|
||||
Overlays.deleteOverlay(loadingSphereID);
|
||||
Overlays.deleteOverlay(loadingToTheSpotID);
|
||||
Overlays.deleteOverlay(loadingToTheSpotHoverID);
|
||||
Overlays.deleteOverlay(domainNameTextID);
|
||||
Overlays.deleteOverlay(domainDescription);
|
||||
Overlays.deleteOverlay(domainToolTip);
|
||||
|
|
257
scripts/system/redirectOverlays.js
Normal file
257
scripts/system/redirectOverlays.js
Normal file
|
@ -0,0 +1,257 @@
|
|||
"use strict";
|
||||
(function() {
|
||||
|
||||
var ERROR_MESSAGE_MAP = [
|
||||
"Oops! Protocol version mismatch.",
|
||||
"Oops! Not authorized to join domain.",
|
||||
"Oops! Connection timed out.",
|
||||
"Oops! Something went wrong."
|
||||
];
|
||||
|
||||
var PROTOCOL_VERSION_MISMATCH = 1;
|
||||
var NOT_AUTHORIZED = 3;
|
||||
var TIMEOUT = 5;
|
||||
var hardRefusalErrors = [PROTOCOL_VERSION_MISMATCH,
|
||||
NOT_AUTHORIZED, TIMEOUT];
|
||||
var timer = null;
|
||||
var isErrorState = false;
|
||||
|
||||
function getOopsText() {
|
||||
var error = Window.getLastDomainConnectionError();
|
||||
var errorMessageMapIndex = hardRefusalErrors.indexOf(error);
|
||||
if (error === -1) {
|
||||
// not an error.
|
||||
return "";
|
||||
} else if (errorMessageMapIndex >= 0) {
|
||||
return ERROR_MESSAGE_MAP[errorMessageMapIndex];
|
||||
} else {
|
||||
// some other text.
|
||||
return ERROR_MESSAGE_MAP[4];
|
||||
}
|
||||
};
|
||||
|
||||
var oopsDimensions = {x: 4.2, y: 0.8};
|
||||
|
||||
var redirectOopsText = Overlays.addOverlay("text3d", {
|
||||
name: "oopsText",
|
||||
position: {x: 0, y: 1.6763916015625, z: 1.45927095413208},
|
||||
rotation: {x: -4.57763671875e-05, y: 0.4957197904586792, z: -7.62939453125e-05, w: 0.8684672117233276},
|
||||
text: getOopsText(),
|
||||
textAlpha: 1,
|
||||
backgroundColor: {x: 0, y: 0, z:0},
|
||||
backgroundAlpha: 0,
|
||||
lineHeight: 0.10,
|
||||
leftMargin: 0.538373570564886,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: true,
|
||||
dimensions: oopsDimensions,
|
||||
grabbable: false,
|
||||
});
|
||||
|
||||
var tryAgainImageNeutral = Overlays.addOverlay("image3d", {
|
||||
name: "tryAgainImage",
|
||||
localPosition: {x: -0.6, y: -0.6, z: 0.0},
|
||||
url: Script.resourcesPath() + "images/interstitialPage/button.png",
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: false,
|
||||
grabbable: false,
|
||||
orientation: Overlays.getProperty(redirectOopsText, "orientation"),
|
||||
parentID: redirectOopsText
|
||||
});
|
||||
|
||||
var tryAgainImageHover = Overlays.addOverlay("image3d", {
|
||||
name: "tryAgainImageHover",
|
||||
localPosition: {x: -0.6, y: -0.6, z: 0.0},
|
||||
url: Script.resourcesPath() + "images/interstitialPage/button_hover.png",
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: false,
|
||||
grabbable: false,
|
||||
orientation: Overlays.getProperty(redirectOopsText, "orientation"),
|
||||
parentID: redirectOopsText
|
||||
});
|
||||
|
||||
var tryAgainText = Overlays.addOverlay("text3d", {
|
||||
name: "tryAgainText",
|
||||
localPosition: {x: -0.6, y: -0.962, z: 0.0},
|
||||
text: "Try Again",
|
||||
textAlpha: 1,
|
||||
backgroundAlpha: 0.00393,
|
||||
lineHeight: 0.08,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: true,
|
||||
grabbable: false,
|
||||
orientation: Overlays.getProperty(redirectOopsText, "orientation"),
|
||||
parentID: redirectOopsText
|
||||
});
|
||||
|
||||
var backImageNeutral = Overlays.addOverlay("image3d", {
|
||||
name: "backImage",
|
||||
localPosition: {x: 0.6, y: -0.6, z: 0.0},
|
||||
url: Script.resourcesPath() + "images/interstitialPage/button.png",
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: false,
|
||||
grabbable: false,
|
||||
orientation: Overlays.getProperty(redirectOopsText, "orientation"),
|
||||
parentID: redirectOopsText
|
||||
});
|
||||
|
||||
var backImageHover = Overlays.addOverlay("image3d", {
|
||||
name: "backImageHover",
|
||||
localPosition: {x: 0.6, y: -0.6, z: 0.0},
|
||||
url: Script.resourcesPath() + "images/interstitialPage/button_hover.png",
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: false,
|
||||
grabbable: false,
|
||||
orientation: Overlays.getProperty(redirectOopsText, "orientation"),
|
||||
parentID: redirectOopsText
|
||||
});
|
||||
|
||||
var backText = Overlays.addOverlay("text3d", {
|
||||
name: "backText",
|
||||
localPosition: {x: 0.6, y: -0.962, z: 0.0},
|
||||
text: "Back",
|
||||
textAlpha: 1,
|
||||
backgroundAlpha: 0.00393,
|
||||
lineHeight: 0.08,
|
||||
visible: false,
|
||||
emissive: true,
|
||||
ignoreRayIntersection: true,
|
||||
grabbable: false,
|
||||
orientation: Overlays.getProperty(redirectOopsText, "orientation"),
|
||||
parentID: redirectOopsText
|
||||
});
|
||||
|
||||
function toggleOverlays(isInErrorState) {
|
||||
isErrorState = isInErrorState;
|
||||
if (!isInErrorState) {
|
||||
var properties = {
|
||||
visible: false
|
||||
};
|
||||
|
||||
Overlays.editOverlay(redirectOopsText, properties);
|
||||
Overlays.editOverlay(tryAgainImageNeutral, properties);
|
||||
Overlays.editOverlay(tryAgainImageHover, properties);
|
||||
Overlays.editOverlay(backImageNeutral, properties);
|
||||
Overlays.editOverlay(backImageHover, properties);
|
||||
Overlays.editOverlay(tryAgainText, properties);
|
||||
Overlays.editOverlay(backText, properties);
|
||||
return;
|
||||
}
|
||||
var oopsText = getOopsText();
|
||||
// if oopsText === "", it was a success.
|
||||
var overlaysVisible = (oopsText !== "");
|
||||
// for catching init or if error state were to be different.
|
||||
isErrorState = overlaysVisible;
|
||||
var properties = {
|
||||
visible: overlaysVisible
|
||||
};
|
||||
|
||||
var textWidth = Overlays.textSize(redirectOopsText, oopsText).width;
|
||||
var textOverlayWidth = oopsDimensions.x;
|
||||
|
||||
var oopsTextProperties = {
|
||||
visible: overlaysVisible,
|
||||
text: oopsText,
|
||||
textAlpha: overlaysVisible,
|
||||
// either visible or invisible. 0 doesn't work in Mac.
|
||||
backgroundAlpha: overlaysVisible * 0.00393,
|
||||
leftMargin: (textOverlayWidth - textWidth) / 2
|
||||
};
|
||||
|
||||
var tryAgainTextWidth = Overlays.textSize(tryAgainText, "Try Again").width;
|
||||
var tryAgainImageWidth = Overlays.getProperty(tryAgainImageNeutral, "dimensions").x;
|
||||
|
||||
var tryAgainTextProperties = {
|
||||
visible: overlaysVisible,
|
||||
leftMargin: (tryAgainImageWidth - tryAgainTextWidth) / 2
|
||||
};
|
||||
|
||||
var backTextWidth = Overlays.textSize(backText, "Back").width;
|
||||
var backImageWidth = Overlays.getProperty(backImageNeutral, "dimensions").x;
|
||||
|
||||
var backTextProperties = {
|
||||
visible: overlaysVisible,
|
||||
leftMargin: (backImageWidth - backTextWidth) / 2
|
||||
};
|
||||
|
||||
Overlays.editOverlay(redirectOopsText, oopsTextProperties);
|
||||
Overlays.editOverlay(tryAgainImageNeutral, properties);
|
||||
Overlays.editOverlay(backImageNeutral, properties);
|
||||
Overlays.editOverlay(tryAgainImageHover, {visible: false});
|
||||
Overlays.editOverlay(backImageHover, {visible: false});
|
||||
Overlays.editOverlay(tryAgainText, tryAgainTextProperties);
|
||||
Overlays.editOverlay(backText, backTextProperties);
|
||||
|
||||
}
|
||||
|
||||
function clickedOnOverlay(overlayID, event) {
|
||||
if (event.isRightButton) {
|
||||
// don't allow right-clicks.
|
||||
return;
|
||||
}
|
||||
if (tryAgainImageHover === overlayID) {
|
||||
location.goToLastAddress();
|
||||
} else if (backImageHover === overlayID && location.canGoBack()) {
|
||||
location.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Script.clearInterval(timer);
|
||||
timer = null;
|
||||
Overlays.deleteOverlay(redirectOopsText);
|
||||
Overlays.deleteOverlay(tryAgainImageNeutral);
|
||||
Overlays.deleteOverlay(backImageNeutral);
|
||||
Overlays.deleteOverlay(tryAgainImageHover);
|
||||
Overlays.deleteOverlay(backImageHover);
|
||||
Overlays.deleteOverlay(tryAgainText);
|
||||
Overlays.deleteOverlay(backText);
|
||||
}
|
||||
|
||||
toggleOverlays(true);
|
||||
|
||||
Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay);
|
||||
Overlays.hoverEnterOverlay.connect(function(overlayID, event) {
|
||||
if (!isErrorState) {
|
||||
// don't allow hover overlay events to get caught if it's not in error state anymore.
|
||||
return;
|
||||
}
|
||||
if (overlayID === backImageNeutral && location.canGoBack()) {
|
||||
Overlays.editOverlay(backImageNeutral, {visible: false});
|
||||
Overlays.editOverlay(backImageHover, {visible: true});
|
||||
}
|
||||
if (overlayID === tryAgainImageNeutral) {
|
||||
Overlays.editOverlay(tryAgainImageNeutral, {visible: false});
|
||||
Overlays.editOverlay(tryAgainImageHover, {visible: true});
|
||||
}
|
||||
});
|
||||
|
||||
Overlays.hoverLeaveOverlay.connect(function(overlayID, event) {
|
||||
if (!isErrorState) {
|
||||
// don't allow hover overlay events to get caught if it's not in error state anymore.
|
||||
return;
|
||||
}
|
||||
if (overlayID === backImageHover) {
|
||||
Overlays.editOverlay(backImageHover, {visible: false});
|
||||
Overlays.editOverlay(backImageNeutral, {visible: true});
|
||||
}
|
||||
if (overlayID === tryAgainImageHover) {
|
||||
Overlays.editOverlay(tryAgainImageHover, {visible: false});
|
||||
Overlays.editOverlay(tryAgainImageNeutral, {visible: true});
|
||||
}
|
||||
});
|
||||
|
||||
Window.redirectErrorStateChanged.connect(toggleOverlays);
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
}());
|
Loading…
Reference in a new issue