mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Merge branch 'master' of github.com:highfidelity/hifi
This commit is contained in:
commit
08ebd51559
69 changed files with 1163 additions and 674 deletions
|
@ -29,16 +29,6 @@ WebView {
|
|||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
if (feature == 2) { // QWebEnginePage::MediaAudioCapture
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
} else {
|
||||
permissionsBar.securityOrigin = securityOrigin;
|
||||
permissionsBar.feature = feature;
|
||||
parentRoot.showPermissionsBar();
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
|
||||
addressBar.text = loadRequest.url
|
||||
|
|
|
@ -84,7 +84,7 @@ Item {
|
|||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
grantFeaturePermission(securityOrigin, feature, false);
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
|
|
|
@ -141,7 +141,7 @@ Item {
|
|||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
grantFeaturePermission(securityOrigin, feature, false);
|
||||
}
|
||||
|
||||
//disable popup
|
||||
|
|
|
@ -35,4 +35,8 @@ WebEngineView {
|
|||
}
|
||||
|
||||
WebSpinner { }
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,6 +182,7 @@ Flickable {
|
|||
|
||||
ColumnLayout {
|
||||
id: micControlsSwitchGroup
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
|
||||
|
||||
SimplifiedControls.Switch {
|
||||
|
@ -205,6 +206,22 @@ Flickable {
|
|||
AudioScriptingInterface.pushToTalkDesktop = !AudioScriptingInterface.pushToTalkDesktop;
|
||||
}
|
||||
}
|
||||
|
||||
SimplifiedControls.Switch {
|
||||
id: attenuateOutputSwitch
|
||||
enabled: AudioScriptingInterface.pushToTalkDesktop
|
||||
Layout.preferredHeight: 18
|
||||
Layout.preferredWidth: parent.width
|
||||
labelTextOn: "Reduce volume of other sounds while I'm pushing-to-talk"
|
||||
checked: AudioScriptingInterface.pushingToTalkOutputGainDesktop !== 0.0
|
||||
onClicked: {
|
||||
if (AudioScriptingInterface.pushingToTalkOutputGainDesktop === 0.0) {
|
||||
AudioScriptingInterface.pushingToTalkOutputGainDesktop = -20.0;
|
||||
} else {
|
||||
AudioScriptingInterface.pushingToTalkOutputGainDesktop = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@ Flickable {
|
|||
|
||||
ColumnLayout {
|
||||
id: micControlsSwitchGroup
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
|
||||
|
||||
SimplifiedControls.Switch {
|
||||
|
|
|
@ -72,10 +72,14 @@ QtObject {
|
|||
readonly property color border: "#00B4EF"
|
||||
}
|
||||
}
|
||||
readonly property QtObject text: QtObject {
|
||||
readonly property color enabled: "#FFFFFF"
|
||||
readonly property color disabled: "#8F8F8F"
|
||||
}
|
||||
}
|
||||
readonly property QtObject simplifiedSwitch: QtObject {
|
||||
readonly property QtObject background: QtObject {
|
||||
readonly property color disabled: "#616161"
|
||||
readonly property color disabled: "#2B2B2B"
|
||||
readonly property color off: "#616161"
|
||||
readonly property color hover: "#616161"
|
||||
readonly property color pressed: "#616161"
|
||||
|
|
|
@ -44,7 +44,7 @@ Item {
|
|||
anchors.bottom: parent.bottom
|
||||
horizontalAlignment: Text.AlignRight
|
||||
visible: sliderText.text != ""
|
||||
color: simplifiedUI.colors.text.white
|
||||
color: root.enabled ? simplifiedUI.colors.controls.slider.text.enabled : simplifiedUI.colors.controls.slider.text.disabled
|
||||
size: simplifiedUI.sizes.controls.slider.labelTextSize
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ Item {
|
|||
innerSwitchHandle.color = simplifiedUI.colors.controls.simplifiedSwitch.handle.disabled;
|
||||
return;
|
||||
}
|
||||
|
||||
if (originalSwitch.checked) {
|
||||
if (originalSwitch.hovered) {
|
||||
innerSwitchHandle.color = simplifiedUI.colors.controls.simplifiedSwitch.handle.hover;
|
||||
|
@ -69,6 +70,10 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
onEnabledChanged: {
|
||||
originalSwitch.changeColor();
|
||||
}
|
||||
|
||||
onCheckedChanged: {
|
||||
originalSwitch.changeColor();
|
||||
}
|
||||
|
@ -88,7 +93,8 @@ Item {
|
|||
background: Rectangle {
|
||||
id: switchBackground
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: originalSwitch.checked ? simplifiedUI.colors.controls.simplifiedSwitch.background.on : simplifiedUI.colors.controls.simplifiedSwitch.background.off
|
||||
color: originalSwitch.enabled ? (originalSwitch.checked ? simplifiedUI.colors.controls.simplifiedSwitch.background.on :
|
||||
simplifiedUI.colors.controls.simplifiedSwitch.background.off) : simplifiedUI.colors.controls.simplifiedSwitch.background.disabled
|
||||
width: originalSwitch.width
|
||||
height: simplifiedUI.sizes.controls.simplifiedSwitch.switchBackgroundHeight
|
||||
radius: height/2
|
||||
|
@ -113,7 +119,7 @@ Item {
|
|||
height: width
|
||||
radius: width/2
|
||||
color: "transparent"
|
||||
border.width: simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleBorderSize
|
||||
border.width: originalSwitch.enabled ? simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleBorderSize : 0
|
||||
border.color: simplifiedUI.colors.controls.simplifiedSwitch.handle.activeBorder
|
||||
}
|
||||
|
||||
|
@ -124,7 +130,7 @@ Item {
|
|||
height: width
|
||||
radius: width/2
|
||||
color: simplifiedUI.colors.controls.simplifiedSwitch.handle.off
|
||||
border.width: originalSwitch.pressed || originalSwitch.checked ? simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleBorderSize : 0
|
||||
border.width: (originalSwitch.pressed || originalSwitch.checked) && originalSwitch.enabled ? simplifiedUI.sizes.controls.simplifiedSwitch.switchHandleBorderSize : 0
|
||||
border.color: originalSwitch.pressed ? simplifiedUI.colors.controls.simplifiedSwitch.handle.activeBorder : simplifiedUI.colors.controls.simplifiedSwitch.handle.checkedBorder
|
||||
|
||||
Component.onCompleted: {
|
||||
|
@ -145,7 +151,7 @@ Item {
|
|||
id: labelOff
|
||||
text: ""
|
||||
size: simplifiedUI.sizes.controls.simplifiedSwitch.labelTextSize
|
||||
color: originalSwitch.checked ? simplifiedUI.colors.controls.simplifiedSwitch.text.off : simplifiedUI.colors.controls.simplifiedSwitch.text.on
|
||||
color: originalSwitch.checked && !originalSwitch.enabled ? simplifiedUI.colors.controls.simplifiedSwitch.text.off : simplifiedUI.colors.controls.simplifiedSwitch.text.on
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: -2 // Necessary for text alignment
|
||||
anchors.bottom: parent.bottom
|
||||
|
@ -193,7 +199,7 @@ Item {
|
|||
id: labelOn
|
||||
text: ""
|
||||
size: simplifiedUI.sizes.controls.simplifiedSwitch.labelTextSize
|
||||
color: originalSwitch.checked ? simplifiedUI.colors.controls.simplifiedSwitch.text.on : simplifiedUI.colors.controls.simplifiedSwitch.text.off
|
||||
color: originalSwitch.checked && originalSwitch.enabled ? simplifiedUI.colors.controls.simplifiedSwitch.text.on : simplifiedUI.colors.controls.simplifiedSwitch.text.off
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: -2 // Necessary for text alignment
|
||||
anchors.left: parent.left
|
||||
|
|
|
@ -2498,6 +2498,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
pauseUntilLoginDetermined();
|
||||
}
|
||||
|
||||
void Application::setFailedToConnectToEntityServer() {
|
||||
_failedToConnectToEntityServer = true;
|
||||
}
|
||||
|
||||
void Application::updateVerboseLogging() {
|
||||
auto menu = Menu::getInstance();
|
||||
if (!menu) {
|
||||
|
@ -3081,7 +3085,22 @@ void Application::showLoginScreen() {
|
|||
#endif
|
||||
}
|
||||
|
||||
static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://content.highfidelity.com/Experiences/Releases" };
|
||||
|
||||
void Application::initializeUi() {
|
||||
|
||||
// Allow remote QML content from trusted sources ONLY
|
||||
{
|
||||
auto defaultUrlValidator = OffscreenQmlSurface::getUrlValidator();
|
||||
auto newValidator = [=](const QUrl& url)->bool {
|
||||
if (AUTHORIZED_EXTERNAL_QML_SOURCE.isParentOf(url)) {
|
||||
return true;
|
||||
}
|
||||
return defaultUrlValidator(url);
|
||||
};
|
||||
OffscreenQmlSurface::setUrlValidator(newValidator);
|
||||
}
|
||||
|
||||
AddressBarDialog::registerType();
|
||||
ErrorDialog::registerType();
|
||||
LoginDialog::registerType();
|
||||
|
@ -5912,7 +5931,7 @@ void Application::resetPhysicsReadyInformation() {
|
|||
_gpuTextureMemSizeStabilityCount = 0;
|
||||
_gpuTextureMemSizeAtLastCheck = 0;
|
||||
_physicsEnabled = false;
|
||||
_octreeProcessor.startEntitySequence();
|
||||
_octreeProcessor.startSafeLanding();
|
||||
}
|
||||
|
||||
|
||||
|
@ -6162,6 +6181,24 @@ void Application::updateSecondaryCameraViewFrustum() {
|
|||
|
||||
static bool domainLoadingInProgress = false;
|
||||
|
||||
void Application::tryToEnablePhysics() {
|
||||
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
|
||||
if (gpuTextureMemSizeStable() || !enableInterstitial) {
|
||||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::update(float deltaTime) {
|
||||
PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine._renderFrameCount + 1);
|
||||
|
||||
|
@ -6169,7 +6206,6 @@ void Application::update(float deltaTime) {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!_physicsEnabled) {
|
||||
if (!domainLoadingInProgress) {
|
||||
PROFILE_ASYNC_BEGIN(app, "Scene Loading", "");
|
||||
|
@ -6178,24 +6214,16 @@ void Application::update(float deltaTime) {
|
|||
|
||||
// we haven't yet enabled physics. we wait until we think we have all the collision information
|
||||
// for nearby entities before starting bullet up.
|
||||
quint64 now = usecTimestampNow();
|
||||
if (isServerlessMode() || _octreeProcessor.isLoadSequenceComplete()) {
|
||||
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();
|
||||
}
|
||||
if (isServerlessMode()) {
|
||||
tryToEnablePhysics();
|
||||
} else if (_failedToConnectToEntityServer) {
|
||||
if (_octreeProcessor.safeLandingIsActive()) {
|
||||
_octreeProcessor.stopSafeLanding();
|
||||
}
|
||||
} else {
|
||||
_octreeProcessor.updateSafeLanding();
|
||||
if (_octreeProcessor.safeLandingIsComplete()) {
|
||||
tryToEnablePhysics();
|
||||
}
|
||||
}
|
||||
} else if (domainLoadingInProgress) {
|
||||
|
@ -7144,13 +7172,17 @@ void Application::resettingDomain() {
|
|||
clearDomainOctreeDetails(false);
|
||||
}
|
||||
|
||||
void Application::nodeAdded(SharedNodePointer node) const {
|
||||
void Application::nodeAdded(SharedNodePointer node) {
|
||||
if (node->getType() == NodeType::EntityServer) {
|
||||
if (!_failedToConnectToEntityServer) {
|
||||
if (_failedToConnectToEntityServer && !_entityServerConnectionTimer.isActive()) {
|
||||
_failedToConnectToEntityServer = false;
|
||||
_octreeProcessor.stopSafeLanding();
|
||||
_octreeProcessor.startSafeLanding();
|
||||
} else if (_entityServerConnectionTimer.isActive()) {
|
||||
_entityServerConnectionTimer.stop();
|
||||
_entityServerConnectionTimer.setInterval(ENTITY_SERVER_CONNECTION_TIMEOUT);
|
||||
_entityServerConnectionTimer.start();
|
||||
}
|
||||
_entityServerConnectionTimer.setInterval(ENTITY_SERVER_CONNECTION_TIMEOUT);
|
||||
_entityServerConnectionTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7160,7 +7192,6 @@ void Application::nodeActivated(SharedNodePointer node) {
|
|||
|
||||
#if !defined(DISABLE_QML)
|
||||
auto offscreenUi = getOffscreenUI();
|
||||
|
||||
if (offscreenUi) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
|
|
@ -512,7 +512,7 @@ private slots:
|
|||
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
void setFailedToConnectToEntityServer() { _failedToConnectToEntityServer = true; }
|
||||
void setFailedToConnectToEntityServer();
|
||||
|
||||
bool acceptSnapshot(const QString& urlString);
|
||||
bool askToSetAvatarUrl(const QString& url);
|
||||
|
@ -527,7 +527,7 @@ private slots:
|
|||
|
||||
void domainURLChanged(QUrl domainURL);
|
||||
void updateWindowTitle() const;
|
||||
void nodeAdded(SharedNodePointer node) const;
|
||||
void nodeAdded(SharedNodePointer node);
|
||||
void nodeActivated(SharedNodePointer node);
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
static void packetSent(quint64 length);
|
||||
|
@ -564,6 +564,7 @@ private:
|
|||
void cleanupBeforeQuit();
|
||||
|
||||
void idle();
|
||||
void tryToEnablePhysics();
|
||||
void update(float deltaTime);
|
||||
|
||||
// Various helper functions called during update()
|
||||
|
@ -786,8 +787,6 @@ private:
|
|||
qint64 _gpuTextureMemSizeStabilityCount { 0 };
|
||||
qint64 _gpuTextureMemSizeAtLastCheck { 0 };
|
||||
|
||||
quint64 _lastPhysicsCheckTime { usecTimestampNow() }; // when did we last check to see if physics was ready
|
||||
|
||||
bool _keyboardDeviceHasFocus { true };
|
||||
|
||||
ConnectionMonitor _connectionMonitor;
|
||||
|
|
|
@ -457,12 +457,14 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
* @property {number} timeScale=3.4e+38 - Controls how long it takes for the entity's position and rotation to catch up with
|
||||
* the target. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action is
|
||||
* applied using an exponential decay.
|
||||
* @property {boolean} kinematic=false - If <code>true</code>, the entity is made kinematic during the action; the entity won't
|
||||
* lag behind the hand but constraint actions such as <code>"hinge"</code> won't act properly.
|
||||
* @property {boolean} kinematicSetVelocity=false - If <code>true</code> and <code>kinematic</code> is <code>true</code>, the
|
||||
* entity's <code>velocity</code> property will be set during the action, e.g., so that other scripts may use the value.
|
||||
* @property {boolean} ignoreIK=false - If <code>true</code>, the entity follows the HMD controller rather than the avatar's
|
||||
* hand.
|
||||
* @property {boolean} kinematic=false - <code>true</code> if the entity is made kinematic during the action; the entity won't
|
||||
* lag behind the hand but constraint actions such as <code>"hinge"</code> won't act properly. <code>false</code> if the
|
||||
* entity is not made kinematic during the action
|
||||
* @property {boolean} kinematicSetVelocity=false - <code>true</code> if, when <code>kinematic</code> is <code>true</code>, the
|
||||
* entity's velocity will be set during the action, e.g., so that other scripts may use the value. <code>false</code> if
|
||||
* the entity's velocity will not be set during the action.
|
||||
* @property {boolean} ignoreIK=false - <code>true</code> if the entity follows the HMD controller, <code>false</code> if it
|
||||
* follows the avatar's hand.
|
||||
*/
|
||||
QVariantMap AvatarActionHold::getArguments() {
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
|
|
|
@ -63,7 +63,6 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
// construct a new packet from the piggybacked one
|
||||
auto buffer = std::unique_ptr<char[]>(new char[piggybackBytes]);
|
||||
memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggybackBytes);
|
||||
|
||||
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, message->getSenderSockAddr());
|
||||
message = QSharedPointer<ReceivedMessage>::create(*newPacket);
|
||||
} else {
|
||||
|
@ -80,7 +79,6 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
|
||||
const QUuid& senderUUID = sendingNode->getUUID();
|
||||
if (!versionDebugSuppressMap.contains(senderUUID, packetType)) {
|
||||
|
||||
qDebug() << "Was stats packet? " << wasStatsPacket;
|
||||
qDebug() << "OctreePacketProcessor - piggyback packet version mismatch on" << packetType << "- Sender"
|
||||
<< senderUUID << "sent" << (int) message->getVersion() << "but"
|
||||
|
@ -115,7 +113,9 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
auto renderer = qApp->getEntities();
|
||||
if (renderer) {
|
||||
renderer->processDatagram(*message, sendingNode);
|
||||
_safeLanding->noteReceivedsequenceNumber(renderer->getLastOctreeMessageSequence());
|
||||
if (_safeLanding && _safeLanding->isTracking()) {
|
||||
_safeLanding->addToSequence(renderer->getLastOctreeMessageSequence());
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
@ -124,7 +124,9 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
// Read sequence #
|
||||
OCTREE_PACKET_SEQUENCE completionNumber;
|
||||
message->readPrimitive(&completionNumber);
|
||||
_safeLanding->setCompletionSequenceNumbers(0, completionNumber);
|
||||
if (_safeLanding) {
|
||||
_safeLanding->finishSequence(0, completionNumber);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: {
|
||||
|
@ -133,6 +135,31 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
}
|
||||
}
|
||||
|
||||
void OctreePacketProcessor::startEntitySequence() {
|
||||
_safeLanding->startEntitySequence(qApp->getEntities());
|
||||
void OctreePacketProcessor::startSafeLanding() {
|
||||
if (_safeLanding) {
|
||||
_safeLanding->startTracking(qApp->getEntities());
|
||||
}
|
||||
}
|
||||
|
||||
void OctreePacketProcessor::updateSafeLanding() {
|
||||
if (_safeLanding) {
|
||||
_safeLanding->updateTracking();
|
||||
}
|
||||
}
|
||||
|
||||
void OctreePacketProcessor::stopSafeLanding() {
|
||||
if (_safeLanding) {
|
||||
_safeLanding->stopTracking();
|
||||
}
|
||||
}
|
||||
|
||||
bool OctreePacketProcessor::safeLandingIsActive() const {
|
||||
return _safeLanding && _safeLanding->isTracking();
|
||||
}
|
||||
|
||||
bool OctreePacketProcessor::safeLandingIsComplete() const {
|
||||
if (_safeLanding) {
|
||||
return _safeLanding->trackingIsComplete();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -25,8 +25,12 @@ public:
|
|||
OctreePacketProcessor();
|
||||
~OctreePacketProcessor();
|
||||
|
||||
void startEntitySequence();
|
||||
bool isLoadSequenceComplete() const { return _safeLanding->isLoadSequenceComplete(); }
|
||||
void startSafeLanding();
|
||||
void updateSafeLanding();
|
||||
void stopSafeLanding();
|
||||
bool safeLandingIsActive() const;
|
||||
bool safeLandingIsComplete() const;
|
||||
|
||||
float domainLoadingProgress() const { return _safeLanding->loadingProgressPercentage(); }
|
||||
|
||||
signals:
|
||||
|
|
|
@ -33,61 +33,45 @@ bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const
|
|||
return lessThanWraparound<OCTREE_PACKET_SEQUENCE>(a, b);
|
||||
}
|
||||
|
||||
void SafeLanding::startEntitySequence(QSharedPointer<EntityTreeRenderer> entityTreeRenderer) {
|
||||
|
||||
void SafeLanding::startTracking(QSharedPointer<EntityTreeRenderer> entityTreeRenderer) {
|
||||
if (!entityTreeRenderer.isNull()) {
|
||||
auto entityTree = entityTreeRenderer->getTree();
|
||||
if (entityTree) {
|
||||
if (entityTree && !_trackingEntities) {
|
||||
Locker lock(_lock);
|
||||
_entityTreeRenderer = entityTreeRenderer;
|
||||
_trackedEntities.clear();
|
||||
_trackingEntities = true;
|
||||
_maxTrackedEntityCount = 0;
|
||||
_initialStart = INVALID_SEQUENCE;
|
||||
_initialEnd = INVALID_SEQUENCE;
|
||||
_sequenceNumbers.clear();
|
||||
_trackingEntities = true;
|
||||
_startTime = usecTimestampNow();
|
||||
|
||||
connect(std::const_pointer_cast<EntityTree>(entityTree).get(),
|
||||
&EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity, Qt::DirectConnection);
|
||||
connect(std::const_pointer_cast<EntityTree>(entityTree).get(),
|
||||
&EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity);
|
||||
|
||||
_sequenceNumbers.clear();
|
||||
_initialStart = INVALID_SEQUENCE;
|
||||
_initialEnd = INVALID_SEQUENCE;
|
||||
_startTime = usecTimestampNow();
|
||||
EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SafeLanding::stopEntitySequence() {
|
||||
Locker lock(_lock);
|
||||
_trackingEntities = false;
|
||||
_maxTrackedEntityCount = 0;
|
||||
_trackedEntityStabilityCount = 0;
|
||||
_initialStart = INVALID_SEQUENCE;
|
||||
_initialEnd = INVALID_SEQUENCE;
|
||||
_trackedEntities.clear();
|
||||
_sequenceNumbers.clear();
|
||||
}
|
||||
|
||||
void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
|
||||
if (_trackingEntities) {
|
||||
if (_trackingEntities && _entityTreeRenderer) {
|
||||
Locker lock(_lock);
|
||||
auto entityTree = _entityTreeRenderer->getTree();
|
||||
if (entityTree) {
|
||||
EntityItemPointer entity = entityTree->findEntityByID(entityID);
|
||||
if (entity && !entity->isLocalEntity() && entity->getCreated() < _startTime) {
|
||||
_trackedEntities.emplace(entityID, entity);
|
||||
|
||||
if (_entityTreeRenderer.isNull() || _entityTreeRenderer->getTree() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityItemPointer entity = _entityTreeRenderer->getTree()->findEntityByID(entityID);
|
||||
|
||||
if (entity && !entity->isLocalEntity() && entity->getCreated() < _startTime) {
|
||||
|
||||
_trackedEntities.emplace(entityID, entity);
|
||||
int trackedEntityCount = (int)_trackedEntities.size();
|
||||
|
||||
if (trackedEntityCount > _maxTrackedEntityCount) {
|
||||
_maxTrackedEntityCount = trackedEntityCount;
|
||||
_trackedEntityStabilityCount = 0;
|
||||
int trackedEntityCount = (int)_trackedEntities.size();
|
||||
if (trackedEntityCount > _maxTrackedEntityCount) {
|
||||
_maxTrackedEntityCount = trackedEntityCount;
|
||||
_trackedEntityStabilityCount = 0;
|
||||
}
|
||||
}
|
||||
//qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,32 +81,94 @@ void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) {
|
|||
_trackedEntities.erase(entityID);
|
||||
}
|
||||
|
||||
void SafeLanding::setCompletionSequenceNumbers(int first, int last) {
|
||||
void SafeLanding::finishSequence(int first, int last) {
|
||||
Locker lock(_lock);
|
||||
if (_initialStart == INVALID_SEQUENCE) {
|
||||
if (_trackingEntities) {
|
||||
_initialStart = first;
|
||||
_initialEnd = last;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) {
|
||||
void SafeLanding::addToSequence(int sequenceNumber) {
|
||||
Locker lock(_lock);
|
||||
if (_trackingEntities) {
|
||||
Locker lock(_lock);
|
||||
_sequenceNumbers.insert(sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
bool SafeLanding::isLoadSequenceComplete() {
|
||||
if ((isEntityLoadingComplete() && isSequenceNumbersComplete()) || qApp->failedToConnectToEntityServer()) {
|
||||
Locker lock(_lock);
|
||||
_initialStart = INVALID_SEQUENCE;
|
||||
_initialEnd = INVALID_SEQUENCE;
|
||||
_entityTreeRenderer.clear();
|
||||
_trackingEntities = false; // Don't track anything else that comes in.
|
||||
EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority);
|
||||
void SafeLanding::updateTracking() {
|
||||
if (!_trackingEntities || !_entityTreeRenderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
return !_trackingEntities;
|
||||
{
|
||||
Locker lock(_lock);
|
||||
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
auto entityMapIter = _trackedEntities.begin();
|
||||
while (entityMapIter != _trackedEntities.end()) {
|
||||
auto entity = entityMapIter->second;
|
||||
bool isVisuallyReady = true;
|
||||
if (enableInterstitial) {
|
||||
auto entityRenderable = _entityTreeRenderer->renderableForEntityId(entityMapIter->first);
|
||||
if (!entityRenderable) {
|
||||
_entityTreeRenderer->addingEntity(entityMapIter->first);
|
||||
}
|
||||
isVisuallyReady = entity->isVisuallyReady() || (!entityRenderable && !entity->isParentPathComplete());
|
||||
}
|
||||
if (isEntityPhysicsReady(entity) && isVisuallyReady) {
|
||||
entityMapIter = _trackedEntities.erase(entityMapIter);
|
||||
} else {
|
||||
if (!isVisuallyReady) {
|
||||
entity->requestRenderUpdate();
|
||||
}
|
||||
entityMapIter++;
|
||||
}
|
||||
}
|
||||
if (enableInterstitial) {
|
||||
_trackedEntityStabilityCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (_trackedEntities.empty()) {
|
||||
// no more tracked entities --> check sequenceNumbers
|
||||
if (_initialStart != INVALID_SEQUENCE) {
|
||||
bool shouldStop = false;
|
||||
{
|
||||
Locker lock(_lock);
|
||||
int sequenceSize = _initialStart <= _initialEnd ? _initialEnd - _initialStart:
|
||||
_initialEnd + SEQUENCE_MODULO - _initialStart;
|
||||
auto startIter = _sequenceNumbers.find(_initialStart);
|
||||
auto endIter = _sequenceNumbers.find(_initialEnd - 1);
|
||||
|
||||
bool missingSequenceNumbers = qApp->isMissingSequenceNumbers();
|
||||
shouldStop = (sequenceSize == 0 ||
|
||||
(startIter != _sequenceNumbers.end() &&
|
||||
endIter != _sequenceNumbers.end() &&
|
||||
((distance(startIter, endIter) == sequenceSize - 1) || !missingSequenceNumbers)));
|
||||
}
|
||||
if (shouldStop) {
|
||||
stopTracking();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SafeLanding::stopTracking() {
|
||||
Locker lock(_lock);
|
||||
_trackingEntities = false;
|
||||
if (_entityTreeRenderer) {
|
||||
auto entityTree = _entityTreeRenderer->getTree();
|
||||
disconnect(std::const_pointer_cast<EntityTree>(entityTree).get(),
|
||||
&EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity);
|
||||
disconnect(std::const_pointer_cast<EntityTree>(entityTree).get(),
|
||||
&EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity);
|
||||
_entityTreeRenderer.reset();
|
||||
}
|
||||
EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority);
|
||||
}
|
||||
|
||||
bool SafeLanding::trackingIsComplete() const {
|
||||
return !_trackingEntities && (_initialStart != INVALID_SEQUENCE);
|
||||
}
|
||||
|
||||
float SafeLanding::loadingProgressPercentage() {
|
||||
|
@ -141,29 +187,6 @@ float SafeLanding::loadingProgressPercentage() {
|
|||
return entityReadyPercentage;
|
||||
}
|
||||
|
||||
bool SafeLanding::isSequenceNumbersComplete() {
|
||||
if (_initialStart != INVALID_SEQUENCE) {
|
||||
Locker lock(_lock);
|
||||
int sequenceSize = _initialStart <= _initialEnd ? _initialEnd - _initialStart:
|
||||
_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) || !missingSequenceNumbers))) {
|
||||
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
if (!enableInterstitial) {
|
||||
_trackingEntities = false; // Don't track anything else that comes in.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeLanding::isEntityPhysicsReady(const EntityItemPointer& entity) {
|
||||
if (entity && !entity->getCollisionless()) {
|
||||
const auto& entityType = entity->getType();
|
||||
|
@ -181,52 +204,9 @@ bool SafeLanding::isEntityPhysicsReady(const EntityItemPointer& entity) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SafeLanding::isEntityLoadingComplete() {
|
||||
Locker lock(_lock);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if (enableInterstitial) {
|
||||
auto entityRenderable = entityTree->renderableForEntityId(entityMapIter->first);
|
||||
if (!entityRenderable) {
|
||||
entityTree->addingEntity(entityMapIter->first);
|
||||
}
|
||||
|
||||
isVisuallyReady = entity->isVisuallyReady() || (!entityRenderable && !entity->isParentPathComplete());
|
||||
}
|
||||
|
||||
if (isEntityPhysicsReady(entity) && isVisuallyReady) {
|
||||
entityMapIter = _trackedEntities.erase(entityMapIter);
|
||||
} else {
|
||||
if (!isVisuallyReady) {
|
||||
entity->requestRenderUpdate();
|
||||
}
|
||||
|
||||
entityMapIter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (enableInterstitial) {
|
||||
_trackedEntityStabilityCount++;
|
||||
}
|
||||
|
||||
|
||||
return _trackedEntities.empty();
|
||||
}
|
||||
|
||||
float SafeLanding::ElevatedPriority(const EntityItem& entityItem) {
|
||||
return entityItem.getCollisionless() ? 0.0f : 10.0f;
|
||||
}
|
||||
|
|
|
@ -25,11 +25,14 @@ class EntityItemID;
|
|||
|
||||
class SafeLanding : public QObject {
|
||||
public:
|
||||
void startEntitySequence(QSharedPointer<EntityTreeRenderer> entityTreeRenderer);
|
||||
void stopEntitySequence();
|
||||
void setCompletionSequenceNumbers(int first, int last); // 'last' exclusive.
|
||||
void noteReceivedsequenceNumber(int sequenceNumber);
|
||||
bool isLoadSequenceComplete();
|
||||
void startTracking(QSharedPointer<EntityTreeRenderer> entityTreeRenderer);
|
||||
void updateTracking();
|
||||
void stopTracking();
|
||||
bool isTracking() const { return _trackingEntities; }
|
||||
bool trackingIsComplete() const;
|
||||
|
||||
void finishSequence(int first, int last); // 'last' exclusive.
|
||||
void addToSequence(int sequenceNumber);
|
||||
float loadingProgressPercentage();
|
||||
|
||||
private slots:
|
||||
|
@ -37,10 +40,8 @@ private slots:
|
|||
void deleteTrackedEntity(const EntityItemID& entityID);
|
||||
|
||||
private:
|
||||
bool isSequenceNumbersComplete();
|
||||
bool isEntityPhysicsReady(const EntityItemPointer& entity);
|
||||
void debugDumpSequenceIDs() const;
|
||||
bool isEntityLoadingComplete();
|
||||
|
||||
std::mutex _lock;
|
||||
using Locker = std::lock_guard<std::mutex>;
|
||||
|
|
|
@ -366,8 +366,15 @@ void Audio::onContextChanged() {
|
|||
void Audio::handlePushedToTalk(bool enabled) {
|
||||
if (getPTT()) {
|
||||
if (enabled) {
|
||||
if (!qApp->isHMDMode()) {
|
||||
float gain = resultWithReadLock<float>([&] { return _pttOutputGainDesktop; });
|
||||
// convert dB to amplitude
|
||||
gain = fastExp2f(gain / 6.02059991f);
|
||||
DependencyManager::get<AudioClient>()->setOutputGain(gain); // duck the output by N dB
|
||||
}
|
||||
setMuted(false);
|
||||
} else {
|
||||
DependencyManager::get<AudioClient>()->setOutputGain(1.0f);
|
||||
setMuted(true);
|
||||
}
|
||||
}
|
||||
|
@ -497,3 +504,29 @@ float Audio::getSystemInjectorGain() {
|
|||
return _systemInjectorGain;
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::setPushingToTalkOutputGainDesktop(float gain) {
|
||||
if (gain > 0.0f) {
|
||||
qDebug() << "Denying attempt to set Pushing to Talk Output Gain above 0dB. Attempted value:" << gain;
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (getPushingToTalkOutputGainDesktop() != gain) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
if (_pttOutputGainDesktop != gain) {
|
||||
_pttOutputGainDesktop = gain;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
emit pushingToTalkOutputGainDesktopChanged(gain);
|
||||
}
|
||||
}
|
||||
|
||||
float Audio::getPushingToTalkOutputGainDesktop() {
|
||||
return resultWithReadLock<float>([&] { return _pttOutputGainDesktop; });
|
||||
}
|
||||
|
|
|
@ -66,10 +66,12 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
* @property {boolean} pushToTalkHMD - <code>true</code> if HMD push-to-talk is enabled, otherwise <code>false</code>.
|
||||
* @property {boolean} pushingToTalk - <code>true</code> if the user is currently pushing-to-talk, otherwise
|
||||
* <code>false</code>.
|
||||
* @property {float} avatarGain - The gain (relative volume) that avatars' voices are played at. This gain is used at the server.
|
||||
* @property {float} localInjectorGain - The gain (relative volume) that local injectors (local environment sounds) are played at.
|
||||
* @property {float} serverInjectorGain - The gain (relative volume) that server injectors (server environment sounds) are played at. This gain is used at the server.
|
||||
* @property {float} systemInjectorGain - The gain (relative volume) that system sounds are played at.
|
||||
* @property {number} avatarGain - The gain (relative volume) that avatars' voices are played at. This gain is used at the server.
|
||||
* @property {number} localInjectorGain - The gain (relative volume) that local injectors (local environment sounds) are played at.
|
||||
* @property {number} serverInjectorGain - The gain (relative volume) that server injectors (server environment sounds) are played at. This gain is used at the server.
|
||||
* @property {number} systemInjectorGain - The gain (relative volume) that system sounds are played at.
|
||||
* @property {number} pushingToTalkOutputGainDesktop - The gain (relative volume) that all sounds are played at when the user is holding
|
||||
* the push-to-talk key in Desktop mode.
|
||||
*
|
||||
* @comment The following properties are from AudioScriptingInterface.h.
|
||||
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
|
||||
|
@ -94,6 +96,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged)
|
||||
Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged)
|
||||
Q_PROPERTY(bool pushingToTalk READ getPushingToTalk WRITE setPushingToTalk NOTIFY pushingToTalkChanged)
|
||||
Q_PROPERTY(float pushingToTalkOutputGainDesktop READ getPushingToTalkOutputGainDesktop
|
||||
WRITE setPushingToTalkOutputGainDesktop NOTIFY pushingToTalkOutputGainDesktopChanged)
|
||||
Q_PROPERTY(float avatarGain READ getAvatarGain WRITE setAvatarGain NOTIFY avatarGainChanged)
|
||||
Q_PROPERTY(float localInjectorGain READ getLocalInjectorGain WRITE setLocalInjectorGain NOTIFY localInjectorGainChanged)
|
||||
Q_PROPERTY(float serverInjectorGain READ getInjectorGain WRITE setInjectorGain NOTIFY serverInjectorGainChanged)
|
||||
|
@ -197,14 +201,14 @@ public:
|
|||
/**jsdoc
|
||||
* Sets the gain (relative volume) that avatars' voices are played at. This gain is used at the server.
|
||||
* @function Audio.setAvatarGain
|
||||
* @param {number} gain - Avatar gain (dB) at the server.
|
||||
* @param {number} gain - The avatar gain (dB) at the server.
|
||||
*/
|
||||
Q_INVOKABLE void setAvatarGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the gain (relative volume) that avatars' voices are played at. This gain is used at the server.
|
||||
* @function Audio.getAvatarGain
|
||||
* @returns {number} Avatar gain (dB) at the server.
|
||||
* @returns {number} The avatar gain (dB) at the server.
|
||||
* @example <caption>Report current audio gain settings.</caption>
|
||||
* // 0 value = normal volume; -ve value = quieter; +ve value = louder.
|
||||
* print("Avatar gain: " + Audio.getAvatarGain());
|
||||
|
@ -217,42 +221,42 @@ public:
|
|||
/**jsdoc
|
||||
* Sets the gain (relative volume) that environment sounds from the server are played at.
|
||||
* @function Audio.setInjectorGain
|
||||
* @param {number} gain - Injector gain (dB) at the server.
|
||||
* @param {number} gain - The injector gain (dB) at the server.
|
||||
*/
|
||||
Q_INVOKABLE void setInjectorGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the gain (relative volume) that environment sounds from the server are played at.
|
||||
* @function Audio.getInjectorGain
|
||||
* @returns {number} Injector gain (dB) at the server.
|
||||
* @returns {number} The injector gain (dB) at the server.
|
||||
*/
|
||||
Q_INVOKABLE float getInjectorGain();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the gain (relative volume) that environment sounds from the client are played at.
|
||||
* @function Audio.setLocalInjectorGain
|
||||
* @param {number} gain - Injector gain (dB) in the client.
|
||||
* @param {number} gain - The injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE void setLocalInjectorGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the gain (relative volume) that environment sounds from the client are played at.
|
||||
* @function Audio.getLocalInjectorGain
|
||||
* @returns {number} Injector gain (dB) in the client.
|
||||
* @returns {number} The injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE float getLocalInjectorGain();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the gain (relative volume) that system sounds are played at.
|
||||
* @function Audio.setSystemInjectorGain
|
||||
* @param {number} gain - Injector gain (dB) in the client.
|
||||
* @param {number} gain - The injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE void setSystemInjectorGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the gain (relative volume) that system sounds are played at.
|
||||
* @function Audio.getSystemInjectorGain
|
||||
* @returns {number} Injector gain (dB) in the client.
|
||||
* @returns {number} The injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE float getSystemInjectorGain();
|
||||
|
||||
|
@ -290,6 +294,22 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE bool getRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the output volume gain that will be used when the user is holding the Push to Talk key.
|
||||
* Should be negative.
|
||||
* @function Audio.setPushingToTalkOutputGainDesktop
|
||||
* @param {number} gain - The output volume gain (dB) while using PTT.
|
||||
*/
|
||||
Q_INVOKABLE void setPushingToTalkOutputGainDesktop(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the output volume gain that is used when the user is holding the Push to Talk key.
|
||||
* Should be negative.
|
||||
* @function Audio.getPushingToTalkOutputGainDesktop
|
||||
* @returns {number} gain - The output volume gain (dB) while using PTT.
|
||||
*/
|
||||
Q_INVOKABLE float getPushingToTalkOutputGainDesktop();
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -423,7 +443,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the avatar gain changes.
|
||||
* @function Audio.avatarGainChanged
|
||||
* @param {float} gain - The new avatar gain value.
|
||||
* @param {number} gain - The new avatar gain value.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void avatarGainChanged(float gain);
|
||||
|
@ -431,7 +451,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the local injector gain changes.
|
||||
* @function Audio.localInjectorGainChanged
|
||||
* @param {float} gain - The new local injector gain value.
|
||||
* @param {number} gain - The new local injector gain value.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void localInjectorGainChanged(float gain);
|
||||
|
@ -447,11 +467,19 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the system injector gain changes.
|
||||
* @function Audio.systemInjectorGainChanged
|
||||
* @param {float} gain - The new system injector gain value.
|
||||
* @param {number} gain - The new system injector gain value.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void systemInjectorGainChanged(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the push to talk gain changes.
|
||||
* @function Audio.pushingToTalkOutputGainDesktopChanged
|
||||
* @param {number} gain - The new output gain value.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void pushingToTalkOutputGainDesktopChanged(float gain);
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -478,8 +506,9 @@ private:
|
|||
bool _settingsLoaded { false };
|
||||
float _inputVolume { 1.0f };
|
||||
float _inputLevel { 0.0f };
|
||||
float _localInjectorGain { 0.0f }; // in dB
|
||||
float _systemInjectorGain { 0.0f }; // in dB
|
||||
float _localInjectorGain { 0.0f }; // in dB
|
||||
float _systemInjectorGain { 0.0f }; // in dB
|
||||
float _pttOutputGainDesktop { 0.0f }; // in dB
|
||||
bool _isClipping { false };
|
||||
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
||||
bool _enableWarnWhenMuted { true };
|
||||
|
|
|
@ -117,5 +117,15 @@ InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString&
|
|||
Q_ARG(QVariantMap, properties));
|
||||
return interactiveWindow;
|
||||
}
|
||||
return new InteractiveWindow(sourceUrl, properties);;
|
||||
|
||||
|
||||
// The offscreen surface already validates against non-local QML sources, but we also need to ensure that
|
||||
// if we create top level QML, like dock widgets or other types of QQuickView containing desktop windows
|
||||
// that the source URL is permitted
|
||||
const auto& urlValidator = OffscreenQmlSurface::getUrlValidator();
|
||||
if (!urlValidator(sourceUrl)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new InteractiveWindow(sourceUrl, properties);
|
||||
}
|
||||
|
|
|
@ -638,3 +638,13 @@ void WindowScriptingInterface::setActiveDisplayPlugin(int index) {
|
|||
auto name = PluginManager::getInstance()->getDisplayPlugins().at(index)->getName();
|
||||
qApp->setActiveDisplayPlugin(name);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::openWebBrowser() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "openWebBrowser", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->load("Browser.qml");
|
||||
}
|
||||
|
|
|
@ -608,6 +608,12 @@ public slots:
|
|||
*/
|
||||
void setActiveDisplayPlugin(int index);
|
||||
|
||||
/**jsdoc
|
||||
* Opens a web browser in a pop-up window.
|
||||
* @function Window.openWebBrowser
|
||||
*/
|
||||
void openWebBrowser();
|
||||
|
||||
|
||||
private slots:
|
||||
void onWindowGeometryChanged(const QRect& geometry);
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
* @hifi-avatar
|
||||
*
|
||||
* @property {Uuid} keyboardFocusOverlay - Get or set the {@link Entities.EntityTypes|Web} entity that has keyboard focus.
|
||||
* If no entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid(0)|Uuid.NULL} to
|
||||
* If no entity has keyboard focus, returns <code>null</code>; set to <code>null</code> or {@link Uuid(0)|Uuid.NULL} to
|
||||
* clear keyboard focus.
|
||||
*/
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ set(src_files
|
|||
src/LatestBuildRequest.m
|
||||
src/OrganizationRequest.m
|
||||
src/OrganizationRequest.h
|
||||
src/ImageView.m
|
||||
src/ImageView.h
|
||||
src/Interface.h
|
||||
src/Interface.m
|
||||
src/ErrorViewController.h
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aus-lo-eVi">
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aus-lo-eVi" customClass="ImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="MYh-TA-w2A"/>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iAf-Ee-jJL">
|
||||
<rect key="frame" x="95" y="344" width="325" height="33"/>
|
||||
<rect key="frame" x="95" y="336" width="325" height="41"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Choose a display name" id="GzZ-0d-2JH">
|
||||
<font key="font" metaFont="systemBold" size="28"/>
|
||||
|
@ -70,12 +70,12 @@
|
|||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0CN-kS-xZ5">
|
||||
<rect key="frame" x="66" y="68" width="266" height="17"/>
|
||||
<rect key="frame" x="66" y="68" width="271" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="By signing in, you agree to the High Fidelity" id="zSf-YA-osu">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xeX-qc-ccB" customClass="HFButton">
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eih-a8-Pqa">
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eih-a8-Pqa" customClass="ImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="SuI-2T-YhQ"/>
|
||||
|
@ -36,21 +36,21 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FkC-xi-3UI">
|
||||
<rect key="frame" x="156" y="105" width="192" height="17"/>
|
||||
<rect key="frame" x="156" y="103" width="202" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="We seem to have a problem." id="bX3-v1-LLM">
|
||||
<font key="font" metaFont="system" size="15"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tV0-Su-eLX">
|
||||
<rect key="frame" x="156" y="80" width="192" height="17"/>
|
||||
<rect key="frame" x="156" y="78" width="192" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Please restart Launcher" id="aBw-o2-xZE">
|
||||
<font key="font" metaFont="system" size="15"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Sds-MZ-qhT" customClass="HFButton">
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L56-Jv-0N8">
|
||||
<imageView focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L56-Jv-0N8" customClass="ImageView" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" focusRingType="none" alignment="left" imageScaling="proportionallyDown" id="ONf-d4-EDr"/>
|
||||
|
@ -94,7 +94,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hIC-qf-Abj">
|
||||
<rect key="frame" x="63" y="337" width="384" height="33"/>
|
||||
<rect key="frame" x="63" y="330" width="384" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Please log in" id="YuE-2K-HLT">
|
||||
<font key="font" metaFont="systemBold" size="28"/>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kuY-e2-Hqb">
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kuY-e2-Hqb" customClass="ImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="Abr-HV-cKq"/>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qtD-mb-qqq">
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qtD-mb-qqq" customClass="ImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="ixM-5m-vi6"/>
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface CredentialsRequest : NSObject <NSURLConnectionDelegate> {
|
||||
@interface CredentialsRequest : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate> {
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) NSMutableData* webData;
|
||||
@property (nonatomic, retain) NSString* jsonString;
|
||||
|
||||
- (void) confirmCredentials:(NSString*)username :(NSString*)password;
|
||||
@end
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
#import "Launcher.h"
|
||||
#import "Settings.h"
|
||||
|
||||
@interface CredentialsRequest ()
|
||||
@property (nonatomic, assign) NSMutableData* receivedData;
|
||||
@property (nonatomic, assign) NSInteger statusCode;
|
||||
@end
|
||||
|
||||
@implementation CredentialsRequest
|
||||
|
||||
- (void) confirmCredentials:(NSString*)username :(NSString*)password {
|
||||
|
@ -21,77 +26,70 @@
|
|||
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
|
||||
[request setHTTPBody:postData];
|
||||
|
||||
NSURLSession* session = [NSURLSession sharedSession];
|
||||
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
|
||||
NSLog(@"credentials request finished");
|
||||
NSMutableData* webData = [NSMutableData data];
|
||||
[webData appendData:data];
|
||||
NSString* jsonString = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[data length] encoding:NSUTF8StringEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
||||
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
if (json[@"error"] != nil) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[Settings sharedSettings] login:FALSE];
|
||||
[sharedLauncher setLoginErrorState: CREDENTIALS];
|
||||
[sharedLauncher credentialsAccepted:FALSE];
|
||||
});
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[Settings sharedSettings] login:TRUE];
|
||||
[sharedLauncher setTokenString:jsonString];
|
||||
[sharedLauncher credentialsAccepted:TRUE];
|
||||
});
|
||||
}
|
||||
|
||||
NSLog(@"credentials: connectionDidFinished completed");
|
||||
|
||||
}];
|
||||
|
||||
NSURLSession* session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request];
|
||||
|
||||
[dataTask resume];
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
[self.webData appendData:data];
|
||||
NSLog(@"credentials connection received data");
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
NSLog(@"credentials connection received response");
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
|
||||
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
|
||||
self.receivedData = nil;
|
||||
self.receivedData = [[NSMutableData alloc] init];
|
||||
[self.receivedData setLength:0];
|
||||
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
|
||||
if([ne statusCode] == 200) {
|
||||
NSLog(@"connection state is 200 - all okay");
|
||||
} else {
|
||||
NSLog(@"connection state is NOT 200");
|
||||
[[Launcher sharedLauncher] displayErrorPage];
|
||||
}
|
||||
self.statusCode = [ne statusCode];
|
||||
NSLog(@"Credentials Response status code: %ld", self.statusCode);
|
||||
completionHandler(NSURLSessionResponseAllow);
|
||||
}
|
||||
|
||||
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
NSLog(@"Conn Err: %@", [error localizedDescription]);
|
||||
[[Launcher sharedLauncher] displayErrorPage];
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
|
||||
didReceiveData:(NSData *)data {
|
||||
|
||||
[self.receivedData appendData:data];
|
||||
NSLog(@"Credentials: did recieve data");
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
NSLog(@"credentials request finished");
|
||||
NSString* jsonString = [[NSString alloc] initWithBytes: [self.webData mutableBytes] length:[self.webData length] encoding:NSUTF8StringEncoding];
|
||||
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
if (json[@"error"] != nil) {
|
||||
[[Settings sharedSettings] login:FALSE];
|
||||
[sharedLauncher setLoginErrorState: CREDENTIALS];
|
||||
[sharedLauncher credentialsAccepted:FALSE];
|
||||
if (error) {
|
||||
NSLog(@"Credentials: Request completed with an error -> error: %@", error);
|
||||
[sharedLauncher displayErrorPage];
|
||||
} else {
|
||||
[[Settings sharedSettings] login:TRUE];
|
||||
[sharedLauncher setTokenString:jsonString];
|
||||
[sharedLauncher credentialsAccepted:TRUE];
|
||||
}
|
||||
|
||||
NSLog(@"credentials: connectionDidFinished completed");
|
||||
}
|
||||
if (self.statusCode == 200) {
|
||||
NSString* jsonString = [[NSString alloc] initWithBytes: [self.receivedData mutableBytes] length:[self.receivedData length] encoding:NSUTF8StringEncoding];
|
||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError* jsonError = nil;
|
||||
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
|
||||
|
||||
if (jsonError) {
|
||||
NSLog(@"Credentials: Failed to parse json -> error: %@", jsonError);
|
||||
NSLog(@"Credentials: JSON string from data: %@", jsonString);
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
|
||||
if (json[@"error"] != nil) {
|
||||
NSLog(@"Credentials: Login failed -> error: %@", json[@"error"]);
|
||||
[[Settings sharedSettings] login:FALSE];
|
||||
[sharedLauncher setLoginErrorState: CREDENTIALS];
|
||||
[sharedLauncher credentialsAccepted:FALSE];
|
||||
} else {
|
||||
NSLog(@"Credentials: Login succeeded");
|
||||
[[Settings sharedSettings] login:TRUE];
|
||||
[sharedLauncher setTokenString:jsonString];
|
||||
[sharedLauncher credentialsAccepted:TRUE];
|
||||
}
|
||||
} else if (self.statusCode == 403 || self.statusCode == 404 || self.statusCode == 401) {
|
||||
NSLog(@"Credentials: Log failed with statusCode: %ld", self.statusCode);
|
||||
[[Settings sharedSettings] login:FALSE];
|
||||
[sharedLauncher setLoginErrorState: CREDENTIALS];
|
||||
[sharedLauncher credentialsAccepted:FALSE];
|
||||
} else {
|
||||
[sharedLauncher displayErrorPage];
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:domainContentUrl]
|
||||
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||
timeoutInterval:60.0];
|
||||
|
||||
|
||||
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
|
||||
|
@ -18,7 +18,7 @@
|
|||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
||||
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
|
||||
NSLog(@"domain content downloaded %d%%", (int)(100.0*prog));
|
||||
|
||||
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
|
||||
NSLog(@"Did finish downloading to url");
|
||||
NSError *error;
|
||||
NSError *error = nil;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
|
||||
NSString* finalFilePath = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent:destinationFileName];
|
||||
|
@ -36,18 +36,28 @@
|
|||
{
|
||||
[fileManager removeItemAtURL:destinationURL error:nil];
|
||||
}
|
||||
|
||||
NSLog(@"%@", location.path);
|
||||
NSLog(@"%@", destinationURL);
|
||||
|
||||
NSLog(@"location: %@", location.path);
|
||||
NSLog(@"destination: %@", destinationURL);
|
||||
BOOL success = [fileManager moveItemAtURL:location toURL:destinationURL error:&error];
|
||||
|
||||
|
||||
|
||||
|
||||
NSLog(success ? @"TRUE" : @"FALSE");
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
|
||||
if (error) {
|
||||
NSLog(@"DownlodDomainContent: failed to move file to destintation -> error: %@", error);
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
[sharedLauncher setDownloadContextFilename:destinationFileName];
|
||||
NSLog(@"extracting domain content file");
|
||||
[sharedLauncher extractZipFileAtDestination:[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:@"content"] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:[sharedLauncher getDownloadContentFilename]]];
|
||||
|
||||
BOOL extractionSuccessful = [sharedLauncher extractZipFileAtDestination:[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:@"content"] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:[sharedLauncher getDownloadContentFilename]]];
|
||||
|
||||
if (!extractionSuccessful) {
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
NSLog(@"finished extracting content file");
|
||||
[sharedLauncher domainContentDownloadFinished];
|
||||
}
|
||||
|
|
|
@ -9,18 +9,18 @@
|
|||
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadUrl]
|
||||
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||
timeoutInterval:60.0];
|
||||
|
||||
|
||||
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
|
||||
|
||||
|
||||
[downloadTask resume];
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
||||
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
|
||||
NSLog(@"interface downloaded %d%%", (int)(100.0*prog));
|
||||
|
||||
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
|
||||
NSLog(@"Did finish downloading to url");
|
||||
NSError *error;
|
||||
NSError *error = nil;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
|
||||
NSString* finalFilePath = [[[Launcher sharedLauncher] getAppPath] stringByAppendingPathComponent:destinationFileName];
|
||||
|
@ -39,34 +39,44 @@
|
|||
[fileManager removeItemAtURL:destinationURL error:nil];
|
||||
}
|
||||
[fileManager moveItemAtURL:location toURL:destinationURL error:&error];
|
||||
|
||||
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
|
||||
if (error) {
|
||||
NSLog(@"Download Interface: failed to move file to destination -> error: %@", error);
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
[sharedLauncher setDownloadFilename:destinationFileName];
|
||||
NSString* appPath = [sharedLauncher getAppPath];
|
||||
NSString* downloadFileName = [sharedLauncher getDownloadFilename];
|
||||
|
||||
|
||||
NSLog(@"extract interface zip");
|
||||
[sharedLauncher extractZipFileAtDestination:appPath :[appPath stringByAppendingString:downloadFileName]];
|
||||
BOOL success = [sharedLauncher extractZipFileAtDestination:appPath :[appPath stringByAppendingString:downloadFileName]];
|
||||
if (!success) {
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
NSLog(@"finished extracting interface zip");
|
||||
|
||||
|
||||
NSLog(@"starting xattr");
|
||||
NSTask* quaratineTask = [[NSTask alloc] init];
|
||||
quaratineTask.launchPath = @"/usr/bin/xattr";
|
||||
quaratineTask.arguments = @[@"-d", @"com.apple.quarantine", [appPath stringByAppendingString:@"interface.app"]];
|
||||
|
||||
|
||||
[quaratineTask launch];
|
||||
[quaratineTask waitUntilExit];
|
||||
NSLog(@"finished xattr");
|
||||
|
||||
|
||||
NSString* launcherPath = [appPath stringByAppendingString:@"Launcher"];
|
||||
|
||||
|
||||
[[Settings sharedSettings] setLauncherPath:launcherPath];
|
||||
[sharedLauncher interfaceFinishedDownloading];
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
NSLog(@"completed; error: %@", error);
|
||||
if (error) {
|
||||
NSLog(@"DownloadInterface: did complete with error -> error: %@", error);
|
||||
[[Launcher sharedLauncher] displayErrorPage];
|
||||
}
|
||||
}
|
||||
|
|
6
launchers/darwin/src/ImageView.h
Normal file
6
launchers/darwin/src/ImageView.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface ImageView : NSImageView
|
||||
{
|
||||
}
|
||||
@end
|
11
launchers/darwin/src/ImageView.m
Normal file
11
launchers/darwin/src/ImageView.m
Normal file
|
@ -0,0 +1,11 @@
|
|||
#import "ImageView.h"
|
||||
|
||||
@implementation ImageView
|
||||
|
||||
- (BOOL) mouseDownCanMoveWindow
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -28,12 +28,12 @@
|
|||
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
|
||||
[request setHTTPMethod:@"GET"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
|
||||
// We're using an ephermeral session here to ensure the tags api response is never cached.
|
||||
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
|
||||
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
|
||||
|
||||
|
||||
|
||||
NSLog(@"Latest Build Request error: %@", error);
|
||||
NSLog(@"Latest Build Request Data: %@", data);
|
||||
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
|
||||
|
@ -46,25 +46,25 @@
|
|||
NSLog(@"Latest Build Request -> json string: %@", jsonString);
|
||||
NSError *jsonError = nil;
|
||||
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
|
||||
|
||||
|
||||
if (jsonError) {
|
||||
NSLog(@"Latest Build request: Failed to convert Json to data");
|
||||
}
|
||||
|
||||
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
NSArray *values = [json valueForKey:@"results"];
|
||||
NSDictionary *value = [values objectAtIndex:0];
|
||||
|
||||
|
||||
|
||||
|
||||
NSString* buildNumber = [value valueForKey:@"latest_version"];
|
||||
NSDictionary* installers = [value objectForKey:@"installers"];
|
||||
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
|
||||
NSString* macInstallerUrl = [macInstallerObject valueForKey:@"zip_url"];
|
||||
|
||||
|
||||
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];
|
||||
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
|
||||
NSInteger currentVersion = [self getCurrentVersion];
|
||||
NSLog(@"Latest Build Request -> does build directory exist: %@", appDirectoryExist ? @"TRUE" : @"FALSE");
|
||||
NSLog(@"Latest Build Request -> current version: %ld", currentVersion);
|
||||
|
@ -72,66 +72,13 @@
|
|||
NSLog(@"Latest Build Request -> mac url: %@", macInstallerUrl);
|
||||
BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue);
|
||||
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
|
||||
|
||||
|
||||
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
|
||||
NSLog(@"Latest Build Request -> SHOULD DOWNLOAD: %@", shouldDownloadInterface ? @"TRUE" : @"FALSE");
|
||||
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl];
|
||||
});
|
||||
}];
|
||||
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
//NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
|
||||
|
||||
/*if(theConnection) {
|
||||
self.webData = [NSMutableData data];
|
||||
NSLog(@"connection initiated");
|
||||
}*/
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
[self.webData appendData:data];
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
|
||||
if([ne statusCode] == 200) {
|
||||
NSLog(@"connection state is 200 - all okay");
|
||||
} else {
|
||||
NSLog(@"connection state is NOT 200");
|
||||
[[Launcher sharedLauncher] displayErrorPage];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
NSLog(@"Conn Err: %@", [error localizedDescription]);
|
||||
[[Launcher sharedLauncher] displayErrorPage];
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
self.jsonString = [[NSString alloc] initWithBytes: [self.webData mutableBytes] length:[self.webData length] encoding:NSUTF8StringEncoding];
|
||||
NSData *data = [self.jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
NSArray *values = [json valueForKey:@"results"];
|
||||
NSDictionary *value = [values objectAtIndex:0];
|
||||
|
||||
|
||||
NSString* buildNumber = [value valueForKey:@"latest_version"];
|
||||
NSDictionary* installers = [value objectForKey:@"installers"];
|
||||
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
|
||||
NSString* macInstallerUrl = [macInstallerObject valueForKey:@"zip_url"];
|
||||
|
||||
NSString* interfaceAppPath = [[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"];
|
||||
BOOL appDirectoryExist = [fileManager fileExistsAtPath:interfaceAppPath];
|
||||
|
||||
BOOL latestVersionAvailable = ([self getCurrentVersion] != buildNumber.integerValue);
|
||||
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
|
||||
|
||||
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
|
||||
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -66,7 +66,7 @@ typedef enum LoginErrorTypes
|
|||
- (void) interfaceFinishedDownloading;
|
||||
- (NSString*) getDownloadPathForContentAndScripts;
|
||||
- (void) launchInterface;
|
||||
- (void) extractZipFileAtDestination:(NSString*) destination :(NSString*) file;
|
||||
- (BOOL) extractZipFileAtDestination:(NSString*) destination :(NSString*) file;
|
||||
- (BOOL) isWaitingForInterfaceToTerminate;
|
||||
- (void) setDownloadFilename:(NSString*) aFilename;
|
||||
- (void) setDownloadContextFilename:(NSString*) aFilename;
|
||||
|
|
|
@ -51,11 +51,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
selector:@selector(didTerminateApp:)
|
||||
name:NSWorkspaceDidTerminateApplicationNotification
|
||||
object:nil];
|
||||
|
||||
|
||||
SplashScreen* splashScreen = [[SplashScreen alloc] initWithNibName:@"SplashScreen" bundle:nil];
|
||||
[self.window setContentViewController: splashScreen];
|
||||
[self closeInterfaceIfRunning];
|
||||
|
||||
|
||||
if (!self.waitingForInterfaceToTerminate) {
|
||||
[self checkLoginStatus];
|
||||
}
|
||||
|
@ -65,12 +65,12 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
{
|
||||
NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0]
|
||||
stringByAppendingString:@"/Launcher/"];
|
||||
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
|
||||
NSError * error = nil;
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:TRUE attributes:nil error:&error];
|
||||
}
|
||||
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
|
@ -79,19 +79,26 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/MacOS/"];
|
||||
}
|
||||
|
||||
- (void) extractZipFileAtDestination:(NSString *)destination :(NSString*)file
|
||||
- (BOOL) extractZipFileAtDestination:(NSString *)destination :(NSString*)file
|
||||
{
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
task.launchPath = @"/usr/bin/unzip";
|
||||
task.arguments = @[@"-o", @"-d", destination, file];
|
||||
|
||||
|
||||
[task launch];
|
||||
[task waitUntilExit];
|
||||
|
||||
|
||||
if (DELETE_ZIP_FILES) {
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
[fileManager removeItemAtPath:file error:NULL];
|
||||
}
|
||||
|
||||
if ([task terminationStatus] != 0) {
|
||||
NSLog(@"Extracting file failed -> termination status: %d", [task terminationStatus]);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
- (void) displayErrorPage
|
||||
|
@ -175,7 +182,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
self.domainURL = aDomainURL;
|
||||
self.domainContentUrl = aDomainContentUrl;
|
||||
self.domainScriptsUrl = aDomainScriptsUrl;
|
||||
|
||||
|
||||
[[Settings sharedSettings] setDomainUrl:aDomainURL];
|
||||
}
|
||||
|
||||
|
@ -326,14 +333,14 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
- (void) launchInterface
|
||||
{
|
||||
NSString* launcherPath = [[self getLauncherPath] stringByAppendingString:@"HQ Launcher"];
|
||||
|
||||
|
||||
[[Settings sharedSettings] setLauncherPath:launcherPath];
|
||||
[[Settings sharedSettings] save];
|
||||
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
|
||||
NSURL *url = [NSURL fileURLWithPath:[workspace fullPathForApplication:[[self getAppPath] stringByAppendingString:@"interface.app/Contents/MacOS/interface"]]];
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
|
||||
NSString* contentPath = [[self getDownloadPathForContentAndScripts] stringByAppendingString:@"content"];
|
||||
NSString* displayName = [ self displayName];
|
||||
NSString* scriptsPath = [[self getAppPath] stringByAppendingString:@"interface.app/Contents/Resources/scripts/simplifiedUI/"];
|
||||
|
@ -361,7 +368,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
|
|||
@"--no-launcher", nil];
|
||||
}
|
||||
[workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error];
|
||||
|
||||
|
||||
[NSApp terminate:self];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface OrganizationRequest : NSObject <NSURLConnectionDelegate> {
|
||||
@interface OrganizationRequest : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate> {
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) NSMutableData* webData;
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
static NSString* const organizationURL = @"https://orgs.highfidelity.com/organizations/";
|
||||
|
||||
@interface OrganizationRequest ()
|
||||
@property (nonatomic, assign) NSMutableData* receivedData;
|
||||
@property (nonatomic, assign) NSInteger statusCode;
|
||||
@end
|
||||
|
||||
@implementation OrganizationRequest
|
||||
|
||||
- (void) confirmOrganization:(NSString*)aOrganization :(NSString*)aUsername {
|
||||
|
@ -23,70 +28,72 @@ static NSString* const organizationURL = @"https://orgs.highfidelity.com/organiz
|
|||
}
|
||||
|
||||
NSString* jsonFile = [hash stringByAppendingString:@".json"];
|
||||
NSError *error;
|
||||
NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString:[organizationURL stringByAppendingString:jsonFile]]];
|
||||
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
if (data) {
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
|
||||
[sharedLauncher setDomainURLInfo:[json valueForKey:@"domain"] :[json valueForKey:@"content_set_url"] :[json valueForKey:@"scripts_url"]];
|
||||
[sharedLauncher setLoginErrorState: NONE];
|
||||
return [sharedLauncher organizationRequestFinished:TRUE];
|
||||
}
|
||||
[sharedLauncher setLoginErrorState: ORGANIZATION];
|
||||
return [sharedLauncher organizationRequestFinished:FALSE];
|
||||
/*NSLog(@"URL: %@", [organizationURL stringByAppendingString:jsonFile]);
|
||||
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||
[request setURL:[NSURL URLWithString: [organizationURL stringByAppendingString:@"High%20Fidelity"]]];
|
||||
[request setURL:[NSURL URLWithString:[organizationURL stringByAppendingString:jsonFile]]];
|
||||
[request setHTTPMethod:@"GET"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
[request setValue:@"" forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
|
||||
|
||||
if(theConnection) {
|
||||
self.webData = [NSMutableData data];
|
||||
NSLog(@"connection initiated");
|
||||
}*/
|
||||
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request];
|
||||
[dataTask resume];
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
[self.webData appendData:data];
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
|
||||
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
|
||||
self.receivedData = nil;
|
||||
self.receivedData = [[NSMutableData alloc] init];
|
||||
[self.receivedData setLength:0];
|
||||
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
|
||||
if([ne statusCode] == 200) {
|
||||
NSLog(@"connection state is 200 - all okay");
|
||||
self.statusCode = [ne statusCode];
|
||||
NSLog(@"Organization Response status code: %ld", self.statusCode);
|
||||
completionHandler(NSURLSessionResponseAllow);
|
||||
}
|
||||
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
|
||||
didReceiveData:(NSData *)data {
|
||||
|
||||
[self.receivedData appendData:data];
|
||||
NSLog(@"Organization: did recieve data");
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
if (error) {
|
||||
NSLog(@"Organization: Request completed with an error -> error: %@", error);
|
||||
[sharedLauncher displayErrorPage];
|
||||
} else {
|
||||
NSLog(@"connection state is NOT 200");
|
||||
if (self.statusCode == 200) {
|
||||
NSError *jsonError = nil;
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:self.receivedData options:kNilOptions error:&jsonError];
|
||||
if (jsonError) {
|
||||
NSLog(@"Failed to parse Organzation json -> error: %@", jsonError);
|
||||
[sharedLauncher displayErrorPage];
|
||||
return;
|
||||
}
|
||||
NSString* domainURL = [json valueForKey:@"domain"];
|
||||
NSString* contentSetURL = [json valueForKey:@"content_set_url"];
|
||||
|
||||
if (domainURL != nil && contentSetURL != nil) {
|
||||
NSLog(@"Organization: getting org file successful");
|
||||
[sharedLauncher setDomainURLInfo:[json valueForKey:@"domain"] :[json valueForKey:@"content_set_url"] :nil];
|
||||
[sharedLauncher setLoginErrorState: NONE];
|
||||
[sharedLauncher organizationRequestFinished:TRUE];
|
||||
} else {
|
||||
NSLog(@"Organization: Either domainURL: %@ or contentSetURL: %@ json entries are invalid", domainURL, contentSetURL);
|
||||
[sharedLauncher displayErrorPage];
|
||||
}
|
||||
} else if (self.statusCode == 403 || self.statusCode == 404) {
|
||||
NSLog(@"Organization: failed to get org file");
|
||||
[sharedLauncher setLoginErrorState: ORGANIZATION];
|
||||
[sharedLauncher organizationRequestFinished:FALSE];
|
||||
} else {
|
||||
NSLog(@ "Organization: Response error -> statusCode: %ld", self.statusCode);
|
||||
[sharedLauncher displayErrorPage];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
NSLog(@"Conn Err: %@", [error localizedDescription]);
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
/*NSString* jsonString = [[NSString alloc] initWithBytes: [self.webData mutableBytes] length:[self.webData length] encoding:NSUTF8StringEncoding];
|
||||
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];*/
|
||||
|
||||
/*Launcher* sharedLauncher = [Launcher sharedLauncher];
|
||||
if (json[@"error"] != nil) {
|
||||
NSLog(@"Login in failed");
|
||||
NSString* accessToken = [json objectForKey:@"access_token"];
|
||||
NSLog(@"access token %@", accessToken);
|
||||
[sharedLauncher credentialsAccepted:FALSE];
|
||||
} else {
|
||||
NSLog(@"Login successful");
|
||||
NSString* accessToken = [json objectForKey:@"access_token"];
|
||||
NSLog(@"access token %@", accessToken);
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setValue:accessToken forKey:@"access_token"];
|
||||
[defaults synchronize];
|
||||
[sharedLauncher credentialsAccepted:TRUE];
|
||||
}*/
|
||||
//NSLog(@"OUTPUT:: %@", self.jsonString);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface Window : NSWindow {
|
||||
|
||||
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -4,4 +4,10 @@
|
|||
-(BOOL)canBecomeKeyWindow {
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(BOOL)isMovableByWindowBackground
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -25,28 +25,22 @@ using namespace render;
|
|||
using namespace render::entities;
|
||||
|
||||
static const int FIXED_FONT_POINT_SIZE = 40;
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 92.0f; // Determined through experimentation to fit font to line
|
||||
// height.
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 92.0f; // Determined through experimentation to fit font to line height.
|
||||
const float LINE_SCALE_RATIO = 1.2f;
|
||||
|
||||
TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) :
|
||||
Parent(entity),
|
||||
_textRenderer(TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f)) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
_geometryID = geometryCache->allocateID();
|
||||
}
|
||||
}
|
||||
|
||||
TextEntityRenderer::~TextEntityRenderer() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (_geometryID && geometryCache) {
|
||||
geometryCache->releaseID(_geometryID);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextEntityRenderer::isTransparent() const {
|
||||
return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
return Parent::isTransparent() || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool TextEntityRenderer::isTextTransparent() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return Parent::isTransparent() || _textAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
});
|
||||
}
|
||||
|
||||
Item::Bound TextEntityRenderer::getBound() {
|
||||
|
@ -60,8 +54,12 @@ Item::Bound TextEntityRenderer::getBound() {
|
|||
return bound;
|
||||
}
|
||||
|
||||
ItemKey TextEntityRenderer::getKey() {
|
||||
return ItemKey::Builder(Parent::getKey()).withMetaCullGroup();
|
||||
}
|
||||
|
||||
ShapeKey TextEntityRenderer::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder().withOwnPipeline();
|
||||
auto builder = render::ShapeKey::Builder();
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
|
@ -71,6 +69,15 @@ ShapeKey TextEntityRenderer::getShapeKey() {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
uint32_t TextEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
|
||||
auto parentSubs = Parent::metaFetchMetaSubItems(subItems);
|
||||
if (Item::isValidID(_textRenderID)) {
|
||||
subItems.emplace_back(_textRenderID);
|
||||
return parentSubs + 1;
|
||||
}
|
||||
return parentSubs;
|
||||
}
|
||||
|
||||
bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
if (_text != entity->getText()) {
|
||||
return true;
|
||||
|
@ -134,6 +141,7 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
_dimensions = entity->getScaledDimensions();
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(_dimensions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -152,65 +160,49 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
|
|||
_rightMargin = entity->getRightMargin();
|
||||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
updateTextRenderItem();
|
||||
});
|
||||
}
|
||||
|
||||
void TextEntityRenderer::doRender(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
|
||||
|
||||
glm::vec4 textColor;
|
||||
glm::vec4 backgroundColor;
|
||||
Transform modelTransform;
|
||||
glm::vec3 dimensions;
|
||||
BillboardMode billboardMode;
|
||||
bool forward;
|
||||
withReadLock([&] {
|
||||
modelTransform = _renderTransform;
|
||||
dimensions = _dimensions;
|
||||
billboardMode = _billboardMode;
|
||||
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
textColor = glm::vec4(_textColor, fadeRatio * _textAlpha);
|
||||
textColor = EntityRenderer::calculatePulseColor(textColor, _pulseProperties, _created);
|
||||
backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
|
||||
forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
|
||||
});
|
||||
|
||||
// Render background
|
||||
static const float SLIGHTLY_BEHIND = -0.005f;
|
||||
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
|
||||
|
||||
// Batch render calls
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
// FIXME: we need to find a better way of rendering text so we don't have to do this
|
||||
if (forward) {
|
||||
DependencyManager::get<DeferredLightingEffect>()->setupKeyLightBatch(args, batch);
|
||||
glm::vec4 backgroundColor;
|
||||
Transform modelTransform;
|
||||
BillboardMode billboardMode;
|
||||
PrimitiveMode primitiveMode;
|
||||
RenderLayer renderLayer;
|
||||
withReadLock([&] {
|
||||
modelTransform = _renderTransform;
|
||||
billboardMode = _billboardMode;
|
||||
primitiveMode = _primitiveMode;
|
||||
renderLayer = _renderLayer;
|
||||
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
|
||||
});
|
||||
|
||||
if (backgroundColor.a <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto transformToTopLeft = modelTransform;
|
||||
transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), billboardMode, args->getViewFrustum().getPosition()));
|
||||
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
|
||||
modelTransform.setRotation(EntityItem::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), billboardMode, args->getViewFrustum().getPosition()));
|
||||
batch.setModelTransform(modelTransform);
|
||||
|
||||
if (backgroundColor.a > 0.0f) {
|
||||
batch.setModelTransform(transformToTopLeft);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false, true, forward);
|
||||
geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
render::ShapePipelinePointer pipeline;
|
||||
if (renderLayer == RenderLayer::WORLD && args->_renderMethod != Args::RenderMethod::FORWARD) {
|
||||
pipeline = backgroundColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
|
||||
} else {
|
||||
pipeline = backgroundColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline();
|
||||
}
|
||||
|
||||
if (textColor.a > 0.0f) {
|
||||
// FIXME: Factor out textRenderer so that text parts can be grouped by pipeline for a gpu performance increase.
|
||||
float scale = _lineHeight / _textRenderer->getFontSize();
|
||||
transformToTopLeft.setScale(scale); // Scale to have the correct line height
|
||||
batch.setModelTransform(transformToTopLeft);
|
||||
|
||||
glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin));
|
||||
_textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, forward);
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) {
|
||||
geometryCache->renderWireShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline);
|
||||
} else {
|
||||
geometryCache->renderSolidShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,4 +214,173 @@ QSizeF TextEntityRenderer::textSize(const QString& text) const {
|
|||
float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
|
||||
|
||||
return QSizeF(extents.x, extents.y) * pointToWorldScale;
|
||||
}
|
||||
}
|
||||
|
||||
void TextEntityRenderer::onAddToSceneTyped(const TypedEntityPointer& entity) {
|
||||
Parent::onAddToSceneTyped(entity);
|
||||
_textPayload = std::make_shared<TextPayload>(entity->getID(), _textRenderer);
|
||||
_textRenderID = AbstractViewStateInterface::instance()->getMain3DScene()->allocateID();
|
||||
auto renderPayload = std::make_shared<TextPayload::Payload>(_textPayload);
|
||||
render::Transaction transaction;
|
||||
transaction.resetItem(_textRenderID, renderPayload);
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
updateTextRenderItem();
|
||||
}
|
||||
|
||||
void TextEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
|
||||
Parent::onRemoveFromSceneTyped(entity);
|
||||
render::Transaction transaction;
|
||||
transaction.removeItem(_textRenderID);
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
_textPayload.reset();
|
||||
}
|
||||
|
||||
void TextEntityRenderer::updateTextRenderItem() const {
|
||||
render::Transaction transaction;
|
||||
transaction.updateItem(_textRenderID);
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
entities::TextPayload::TextPayload(const QUuid& entityID, const std::weak_ptr<TextRenderer3D>& textRenderer) :
|
||||
_entityID(entityID), _textRenderer(textRenderer) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
_geometryID = geometryCache->allocateID();
|
||||
}
|
||||
}
|
||||
|
||||
entities::TextPayload::~TextPayload() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (_geometryID && geometryCache) {
|
||||
geometryCache->releaseID(_geometryID);
|
||||
}
|
||||
}
|
||||
|
||||
ItemKey entities::TextPayload::getKey() const {
|
||||
auto renderable = DependencyManager::get<EntityTreeRenderer>()->renderableForEntityId(_entityID);
|
||||
if (renderable) {
|
||||
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
|
||||
ItemKey::Builder key;
|
||||
// Similar to EntityRenderer::getKey()
|
||||
if (textRenderable->isTextTransparent()) {
|
||||
key = ItemKey::Builder::transparentShape().withSubMetaCulled().withTagBits(textRenderable->getTagMask()).withLayer(textRenderable->getHifiRenderLayer());
|
||||
} else if (textRenderable->_canCastShadow) {
|
||||
key = ItemKey::Builder::opaqueShape().withSubMetaCulled().withTagBits(textRenderable->getTagMask()).withShadowCaster().withLayer(textRenderable->getHifiRenderLayer());
|
||||
} else {
|
||||
key = ItemKey::Builder::opaqueShape().withSubMetaCulled().withTagBits(textRenderable->getTagMask()).withLayer(textRenderable->getHifiRenderLayer());
|
||||
}
|
||||
|
||||
if (!textRenderable->_visible) {
|
||||
key.withInvisible();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
}
|
||||
|
||||
Item::Bound entities::TextPayload::getBound() const {
|
||||
auto renderable = DependencyManager::get<EntityTreeRenderer>()->renderableForEntityId(_entityID);
|
||||
if (renderable) {
|
||||
return std::static_pointer_cast<TextEntityRenderer>(renderable)->getBound();
|
||||
}
|
||||
return Item::Bound();
|
||||
}
|
||||
|
||||
ShapeKey entities::TextPayload::getShapeKey() const {
|
||||
auto renderable = DependencyManager::get<EntityTreeRenderer>()->renderableForEntityId(_entityID);
|
||||
if (renderable) {
|
||||
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
|
||||
|
||||
auto builder = render::ShapeKey::Builder().withOwnPipeline();
|
||||
if (textRenderable->isTextTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (textRenderable->_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withWireframe();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return ShapeKey::Builder::invalid();
|
||||
}
|
||||
|
||||
void entities::TextPayload::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("TextPayload::render");
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
auto textRenderer = _textRenderer.lock();
|
||||
if (!textRenderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto renderable = DependencyManager::get<EntityTreeRenderer>()->renderableForEntityId(_entityID);
|
||||
if (!renderable) {
|
||||
return;
|
||||
}
|
||||
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
|
||||
|
||||
glm::vec4 textColor;
|
||||
Transform modelTransform;
|
||||
BillboardMode billboardMode;
|
||||
float lineHeight, leftMargin, rightMargin, topMargin, bottomMargin;
|
||||
QString text;
|
||||
glm::vec3 dimensions;
|
||||
bool forward;
|
||||
textRenderable->withReadLock([&] {
|
||||
modelTransform = textRenderable->_renderTransform;
|
||||
billboardMode = textRenderable->_billboardMode;
|
||||
lineHeight = textRenderable->_lineHeight;
|
||||
leftMargin = textRenderable->_leftMargin;
|
||||
rightMargin = textRenderable->_rightMargin;
|
||||
topMargin = textRenderable->_topMargin;
|
||||
bottomMargin = textRenderable->_bottomMargin;
|
||||
text = textRenderable->_text;
|
||||
dimensions = textRenderable->_dimensions;
|
||||
|
||||
float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f;
|
||||
textColor = glm::vec4(textRenderable->_textColor, fadeRatio * textRenderable->_textAlpha);
|
||||
textColor = EntityRenderer::calculatePulseColor(textColor, textRenderable->_pulseProperties, textRenderable->_created);
|
||||
forward = textRenderable->_renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
|
||||
});
|
||||
|
||||
if (textColor.a <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
modelTransform.setRotation(EntityItem::getBillboardRotation(modelTransform.getTranslation(), modelTransform.getRotation(), billboardMode, args->getViewFrustum().getPosition()));
|
||||
|
||||
float scale = lineHeight / textRenderer->getFontSize();
|
||||
modelTransform.postTranslate(glm::vec3(-0.5, 0.5, 1.0f + EPSILON / dimensions.z));
|
||||
modelTransform.setScale(scale);
|
||||
batch.setModelTransform(modelTransform);
|
||||
|
||||
glm::vec2 bounds = glm::vec2(dimensions.x - (leftMargin + rightMargin), dimensions.y - (topMargin + bottomMargin));
|
||||
textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, text, textColor, bounds / scale, forward);
|
||||
}
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const TextPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
return payload->getKey();
|
||||
}
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
}
|
||||
|
||||
template <> const Item::Bound payloadGetBound(const TextPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
return payload->getBound();
|
||||
}
|
||||
return Item::Bound();
|
||||
}
|
||||
|
||||
template <> const ShapeKey shapeGetShapeKey(const TextPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
return payload->getShapeKey();
|
||||
}
|
||||
return ShapeKey::Builder::invalid();
|
||||
}
|
||||
|
||||
template <> void payloadRender(const TextPayload::Pointer& payload, RenderArgs* args) {
|
||||
return payload->render(args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,19 +19,26 @@ class TextRenderer3D;
|
|||
|
||||
namespace render { namespace entities {
|
||||
|
||||
class TextPayload;
|
||||
|
||||
class TextEntityRenderer : public TypedEntityRenderer<TextEntityItem> {
|
||||
using Parent = TypedEntityRenderer<TextEntityItem>;
|
||||
using Pointer = std::shared_ptr<TextEntityRenderer>;
|
||||
public:
|
||||
TextEntityRenderer(const EntityItemPointer& entity);
|
||||
~TextEntityRenderer();
|
||||
|
||||
QSizeF textSize(const QString& text) const;
|
||||
|
||||
protected:
|
||||
bool isTransparent() const override;
|
||||
bool isTextTransparent() const;
|
||||
Item::Bound getBound() override;
|
||||
ShapeKey getShapeKey() override;
|
||||
ItemKey getKey() override;
|
||||
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
|
||||
|
||||
void onAddToSceneTyped(const TypedEntityPointer& entity) override;
|
||||
void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
private:
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
|
@ -39,7 +46,6 @@ private:
|
|||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
int _geometryID{ 0 };
|
||||
std::shared_ptr<TextRenderer3D> _textRenderer;
|
||||
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
@ -58,8 +64,42 @@ private:
|
|||
|
||||
BillboardMode _billboardMode;
|
||||
glm::vec3 _dimensions;
|
||||
|
||||
std::shared_ptr<TextPayload> _textPayload;
|
||||
render::ItemID _textRenderID;
|
||||
void updateTextRenderItem() const;
|
||||
|
||||
friend class render::entities::TextPayload;
|
||||
};
|
||||
|
||||
class TextPayload {
|
||||
public:
|
||||
TextPayload() = default;
|
||||
TextPayload(const QUuid& entityID, const std::weak_ptr<TextRenderer3D>& textRenderer);
|
||||
~TextPayload();
|
||||
|
||||
typedef render::Payload<TextPayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
ItemKey getKey() const;
|
||||
Item::Bound getBound() const;
|
||||
ShapeKey getShapeKey() const;
|
||||
void render(RenderArgs* args);
|
||||
|
||||
protected:
|
||||
QUuid _entityID;
|
||||
std::weak_ptr<TextRenderer3D> _textRenderer;
|
||||
|
||||
int _geometryID { 0 };
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const entities::TextPayload::Pointer& payload);
|
||||
template <> const Item::Bound payloadGetBound(const entities::TextPayload::Pointer& payload);
|
||||
template <> const ShapeKey shapeGetShapeKey(const entities::TextPayload::Pointer& payload);
|
||||
template <> void payloadRender(const entities::TextPayload::Pointer& payload, RenderArgs* args);
|
||||
}
|
||||
|
||||
#endif // hifi_RenderableTextEntityItem_h
|
||||
|
|
|
@ -57,10 +57,11 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
|
|||
* @property {number} firstFrame=0 - The first frame to play in the animation.
|
||||
* @property {number} lastFrame=100000 - The last frame to play in the animation.
|
||||
* @property {number} currentFrame=0 - The current frame being played in the animation.
|
||||
* @property {boolean} running=false - If <code>true</code> then the animation should play.
|
||||
* @property {boolean} loop=true - If <code>true</code> then the animation is continuously repeated in a loop.
|
||||
* @property {boolean} hold=false - If <code>true</code> then the rotations and translations of the last frame played are
|
||||
* maintained when the animation stops playing.
|
||||
* @property {boolean} running=false - <code>true</code> if the animation should play, <code>false</code> if it shouldn't.
|
||||
* @property {boolean} loop=true - <code>true</code> if the animation is continuously repeated in a loop, <code>false</code> if
|
||||
* it isn't.
|
||||
* @property {boolean} hold=false - <code>true</code> if the rotations and translations of the last frame played are
|
||||
* maintained when the animation stops playing, <code>false</code> if they aren't.
|
||||
*/
|
||||
void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url);
|
||||
|
|
|
@ -109,7 +109,7 @@ variables. These argument variables are used by the code which is run when bull
|
|||
* are disabled during the hold.</td>
|
||||
* <td>{@link Entities.ActionArguments-Hold}</td></tr>
|
||||
* <tr><td><code>"offset"</code></td><td>Object action</td>
|
||||
* <td>Moves an entity so that it is a set distance away from a target point.</td>
|
||||
* <td>Moves an entity so that it is a defined distance away from a target point.</td>
|
||||
* <td>{@link Entities.ActionArguments-Offset}</td></tr>
|
||||
* <tr><td><code>"tractor"</code></td><td>Object action</td>
|
||||
* <td>Moves and rotates an entity to a target position and orientation, optionally relative to another entity.</td>
|
||||
|
|
|
@ -609,8 +609,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
|
||||
/**jsdoc
|
||||
* Different entity types have different properties: some common to all entities (listed in the table) and some specific to
|
||||
* each {@link Entities.EntityType|EntityType} (linked to below). The properties are accessed as an object of property names
|
||||
* and values.
|
||||
* each {@link Entities.EntityType|EntityType} (linked to below).
|
||||
*
|
||||
* @typedef {object} Entities.EntityProperties
|
||||
* @property {Uuid} id - The ID of the entity. <em>Read-only.</em>
|
||||
|
@ -644,16 +643,17 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Uuid} lastEditedBy - The session ID of the avatar or agent that most recently created or edited the entity.
|
||||
* <em>Read-only.</em>
|
||||
*
|
||||
* @property {boolean} locked=false - Whether or not the entity can be edited or deleted. If <code>true</code> then the
|
||||
* entity's properties other than <code>locked</code> cannot be changed, and the entity cannot be deleted.
|
||||
* @property {boolean} visible=true - Whether or not the entity is rendered. If <code>true</code> then the entity is rendered.
|
||||
* @property {boolean} canCastShadow=true - Whether or not the entity can cast a shadow. Currently applicable only to
|
||||
* {@link Entities.EntityProperties-Model|Model} and {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are
|
||||
* cast if inside a {@link Entities.EntityProperties-Zone|Zone} entity with <code>castShadows</code> enabled in its
|
||||
* <code>keyLight</code> property.
|
||||
* @property {boolean} locked=false - <code>true</code> if properties other than <code>locked</code> cannot be changed and the
|
||||
* entity cannot be deleted, <code>false</code> if all properties can be changed and the entity can be deleted.
|
||||
* @property {boolean} visible=true - <code>true</code> if the entity is rendered, <code>false</code> if it isn't.
|
||||
* @property {boolean} canCastShadow=true - <code>true</code> if the entity can cast a shadow, <code>false</code> if it can't.
|
||||
* Currently applicable only to {@link Entities.EntityProperties-Model|Model} and
|
||||
* {@link Entities.EntityProperties-Shape|Shape} entities. Shadows are cast if inside a
|
||||
* {@link Entities.EntityProperties-Zone|Zone} entity with <code>castShadows</code> enabled in its <code>keyLight</code>
|
||||
* property.
|
||||
* @property {boolean} isVisibleInSecondaryCamera=true - <code>true</code> if the entity is rendered in the secondary camera,
|
||||
* <code>false</code> if it isn't.
|
||||
* @property {Entities.RenderLayer} renderLayer="world" - Which layer the entity renders in.
|
||||
* @property {Entities.RenderLayer} renderLayer="world" - The layer that the entity renders in.
|
||||
* @property {Entities.PrimitiveMode} primitiveMode="solid" - How the entity's geometry is rendered.
|
||||
* @property {boolean} ignorePickIntersection=false - <code>true</code> if {@link Picks} and {@link RayPick} ignore the entity,
|
||||
* <code>false</code> if they don't.
|
||||
|
@ -697,20 +697,20 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range <code>0.0</code>
|
||||
* – <code>10.0</code>. The higher the value, the more quickly it slows down. Examples: <code>0.1</code> for ice,
|
||||
* <code>0.9</code> for sandpaper.
|
||||
* @property {number} density=1000 - The density of the entity in kg/m<sup>3</sup>, range <code>100</code> for balsa wood
|
||||
* – <code>10000</code> for silver. The density is used in conjunction with the entity's bounding box volume to work
|
||||
* out its mass in the application of physics.
|
||||
* @property {number} density=1000 - The density of the entity in kg/m<sup>3</sup>, range <code>100</code> –
|
||||
* <code>10000</code>. Examples: <code>100</code> for balsa wood, <code>10000</code> for silver. The density is used in
|
||||
* conjunction with the entity's bounding box volume to work out its mass in the application of physics.
|
||||
*
|
||||
* @property {boolean} collisionless=false - Whether or not the entity should collide with items per its
|
||||
* <code>collisionMask</code> property. If <code>true</code> then the entity does not collide.
|
||||
* @property {boolean} collisionless=false - <code>true</code> if the entity shouldn't collide, <code>false</code> if it
|
||||
* collides with items per its <code>collisionMask</code> property.
|
||||
* @property {boolean} ignoreForCollisions - Synonym for <code>collisionless</code>.
|
||||
* @property {CollisionMask} collisionMask=31 - What types of items the entity should collide with.
|
||||
* @property {string} collidesWith="static,dynamic,kinematic,myAvatar,otherAvatar," - Synonym for <code>collisionMask</code>,
|
||||
* in text format.
|
||||
* @property {string} collisionSoundURL="" - The sound that's played when the entity experiences a collision. Valid file
|
||||
* formats are per {@link SoundObject}.
|
||||
* @property {boolean} dynamic=false - Whether or not the entity should be affected by collisions. If <code>true</code> then
|
||||
* the entity's movement is affected by collisions.
|
||||
* @property {boolean} dynamic=false - <code>true</code> if the entity's movement is affected by collisions, <code>false</code>
|
||||
* if it isn't.
|
||||
* @property {boolean} collisionsWillMove - A synonym for <code>dynamic</code>.
|
||||
*
|
||||
* @property {string} href="" - A "hifi://" metaverse address that a user is teleported to when they click on the entity.
|
||||
|
@ -732,8 +732,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* button beside the "script URL" field in properties tab of the Create app works.
|
||||
* @property {string} serverScripts="" - The URL of the server entity script, if any, that is attached to the entity.
|
||||
*
|
||||
* @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to.
|
||||
* {@link Uuid(0)|Uuid.NULL} if the entity is not parented.
|
||||
* @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that the entity is parented to. A value of
|
||||
* {@link Uuid(0)|Uuid.NULL} is used if the entity is not parented.
|
||||
* @property {number} parentJointIndex=65535 - The joint of the entity or avatar that the entity is parented to. Use
|
||||
* <code>65535</code> or <code>-1</code> to parent to the entity or avatar's position and orientation rather than a joint.
|
||||
* @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented,
|
||||
|
@ -759,19 +759,19 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {string} actionData="" - Base-64 encoded compressed dump of the actions associated with the entity. This property
|
||||
* is typically not used in scripts directly; rather, functions that manipulate an entity's actions update it, e.g.,
|
||||
* {@link Entities.addAction}. The size of this property increases with the number of actions. Because this property value
|
||||
* has to fit within a High Fidelity datagram packet there is a limit to the number of actions that an entity can have, and
|
||||
* has to fit within a High Fidelity datagram packet, there is a limit to the number of actions that an entity can have;
|
||||
* edits which would result in overflow are rejected. <em>Read-only.</em>
|
||||
* @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only
|
||||
* provided for <code>Model</code> entities. <em>Read-only.</em>
|
||||
*
|
||||
* @property {boolean} cloneable=false - If <code>true</code> then the domain or avatar entity can be cloned via
|
||||
* {@link Entities.cloneEntity}.
|
||||
* @property {boolean} cloneable=false - <code>true</code> if the domain or avatar entity can be cloned via
|
||||
* {@link Entities.cloneEntity}, <code>false</code> if it can't be.
|
||||
* @property {number} cloneLifetime=300 - The entity lifetime for clones created from this entity.
|
||||
* @property {number} cloneLimit=0 - The total number of clones of this entity that can exist in the domain at any given time.
|
||||
* @property {boolean} cloneDynamic=false - If <code>true</code> then clones created from this entity will have their
|
||||
* <code>dynamic</code> property set to <code>true</code>.
|
||||
* @property {boolean} cloneAvatarEntity=false - If <code>true</code> then clones created from this entity will be created as
|
||||
* avatar entities.
|
||||
* @property {boolean} cloneDynamic=false - <code>true</code> if clones created from this entity will have their
|
||||
* <code>dynamic</code> property set to <code>true</code>, <code>false</code> if they won't.
|
||||
* @property {boolean} cloneAvatarEntity=false - <code>true</code> if clones created from this entity will be created as
|
||||
* avatar entities, <code>false</code> if they won't be.
|
||||
* @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from.
|
||||
*
|
||||
* @property {Entities.Grab} grab - The entity's grab-related properties.
|
||||
|
@ -833,8 +833,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Color} color=255,255,255 - The color of the light emitted.
|
||||
* @property {number} intensity=1 - The brightness of the light.
|
||||
* @property {number} falloffRadius=0.1 - The distance from the light's center at which intensity is reduced by 25%.
|
||||
* @property {boolean} isSpotlight=false - If <code>true</code> then the light is directional, emitting along the entity's
|
||||
* local negative z-axis; otherwise, the light is a point light which emanates in all directions.
|
||||
* @property {boolean} isSpotlight=false - <code>true</code> if the light is directional, emitting along the entity's
|
||||
* local negative z-axis; <code>false</code> if the light is a point light which emanates in all directions.
|
||||
* @property {number} exponent=0 - Affects the softness of the spotlight beam: the higher the value the softer the beam.
|
||||
* @property {number} cutoff=1.57 - Affects the size of the spotlight beam: the higher the value the larger the beam.
|
||||
* @example <caption>Create a spotlight pointing at the ground.</caption>
|
||||
|
@ -855,7 +855,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
/**jsdoc
|
||||
* The <code>"Line"</code> {@link Entities.EntityType|EntityType} draws thin, straight lines between a sequence of two or more
|
||||
* points. It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.
|
||||
* <p class=important>Deprecated: Use PolyLines instead.</p>
|
||||
* <p class=important>Deprecated: Use {@link Entities.EntityProperties-PolyLine|PolyLine} entities instead.</p>
|
||||
*
|
||||
* @typedef {object} Entities.EntityProperties-Line
|
||||
* @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Must be sufficient to contain all the
|
||||
|
@ -904,7 +904,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* <code>0</code>.
|
||||
* @property {string} parentMaterialName="0" - Selects the mesh part or parts within the parent to which to apply the material.
|
||||
* If in the format <code>"mat::string"</code>, all mesh parts with material name <code>"string"</code> are replaced.
|
||||
* If <code>"all"</code> then all mesh parts are replaced.
|
||||
* If <code>"all"</code>, then all mesh parts are replaced.
|
||||
* Otherwise the property value is parsed as an unsigned integer, specifying the mesh part index to modify.
|
||||
* <p>If the string represents an array (starts with <code>"["</code> and ends with <code>"]"</code>), the string is split
|
||||
* at each <code>","</code> and each element parsed as either a number or a string if it starts with <code>"mat::"</code>.
|
||||
|
@ -919,8 +919,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* <code>{ x: 0, y: 0 }</code> – <code>{ x: 1, y: 1 }</code>.
|
||||
* @property {Vec2} materialMappingScale=1,1 - How much to scale the material within the parent's UV-space.
|
||||
* @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees.
|
||||
* @property {boolean} materialRepeat=true - If <code>true</code>, the material repeats. If <code>false</code>, fragments
|
||||
* outside of texCoord 0 – 1 will be discarded. Works in both <code>"uv"</code> and </code>"projected"</code> modes.
|
||||
* @property {boolean} materialRepeat=true - <code>true</code> if the material repeats, <code>false</code> if it doesn't. If
|
||||
* <code>false</code>, fragments outside of texCoord 0 – 1 will be discarded. Works in both <code>"uv"</code> and
|
||||
* </code>"projected"</code> modes.
|
||||
* @example <caption>Color a sphere using a Material entity.</caption>
|
||||
* var entityID = Entities.addEntity({
|
||||
* type: "Sphere",
|
||||
|
@ -980,7 +981,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to
|
||||
* each joint's parent.
|
||||
* <p>Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or
|
||||
* by setting the value of this property. If you set a joint rotation using this property you also need to set the
|
||||
* by setting the value of this property. If you set a joint rotation using this property, you also need to set the
|
||||
* corresponding <code>jointRotationsSet</code> value to <code>true</code>.</p>
|
||||
* @property {boolean[]} jointRotationsSet=[]] - <code>true</code> values for joints that have had rotations applied,
|
||||
* <code>false</code> otherwise; <code>[]</code> if none are applied or the model hasn't loaded. The array indexes are per
|
||||
|
@ -994,10 +995,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {boolean[]} jointTranslationsSet=[]] - <code>true</code> values for joints that have had translations applied,
|
||||
* <code>false</code> otherwise; <code>[]</code> if none are applied or the model hasn't loaded. The array indexes are per
|
||||
* {@link Entities.getJointIndex|getJointIndex}.
|
||||
* @property {boolean} relayParentJoints=false - If <code>true</code> and the entity is parented to an avatar, then the
|
||||
* avatar's joint rotations are applied to the entity's joints.
|
||||
* @property {boolean} groupCulled=false - If <code>true</code>, the mesh parts of the model are LOD culled as a group.
|
||||
* If <code>false</code>, separate mesh parts will be LOD culled individually.
|
||||
* @property {boolean} relayParentJoints=false - <code>true</code> if when the entity is parented to an avatar, the avatar's
|
||||
* joint rotations are applied to the entity's joints; <code>false</code> if a parent avatar's joint rotations are not
|
||||
* applied to the entity's joints.
|
||||
* @property {boolean} groupCulled=false - <code>true</code> if the mesh parts of the model are LOD culled as a group,
|
||||
* <code>false</code> if separate mesh parts are LOD culled individually.
|
||||
*
|
||||
* @example <caption>Rez a Vive tracker puck.</caption>
|
||||
* var entity = Entities.addEntity({
|
||||
|
@ -1033,8 +1035,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* – <code>{x: 0, y: -8.8, z: 0}</code>.
|
||||
* @property {Vec3} dimensions - The dimensions of the particle effect, i.e., a bounding box containing all the particles
|
||||
* during their lifetimes, assuming that <code>emitterShouldTrail == false</code>. <em>Read-only.</em>
|
||||
* @property {boolean} emitterShouldTrail=false - If <code>true</code> then particles are "left behind" as the emitter moves,
|
||||
* otherwise they stay within the entity's dimensions.
|
||||
* @property {boolean} emitterShouldTrail=false - <code>true</code> if particles are "left behind" as the emitter moves,
|
||||
* <code>false</code> if they stay within the entity's dimensions.
|
||||
*
|
||||
* @property {Quat} emitOrientation=-0.707,0,0,0.707 - The orientation of particle emission relative to the entity's axes. By
|
||||
* default, particles emit along the entity's local z-axis, and <code>azimuthStart</code> and <code>azimuthFinish</code>
|
||||
|
@ -1105,8 +1107,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* <code>2 * Math.PI</code> radians. For example, if <code>particleSpin == Math.PI</code> and
|
||||
* <code>spinSpread == Math.PI / 2</code>, each particle will have a rotation in the range <code>Math.PI / 2</code> –
|
||||
* <code>3 * Math.PI / 2</code>.
|
||||
* @property {boolean} rotateWithEntity=false - <code>true</code> to make the particles' rotations relative to the entity's
|
||||
* instantaneous rotation, <code>false</code> to make them relative to world coordinates. If <code>true</code> with
|
||||
* @property {boolean} rotateWithEntity=false - <code>true</code> if the particles' rotations are relative to the entity's
|
||||
* instantaneous rotation, <code>false</code> if they're relative to world coordinates. If <code>true</code> with
|
||||
* <code>particleSpin == 0</code>, the particles keep oriented per the entity's orientation.
|
||||
*
|
||||
* @example <caption>Create a ball of green smoke.</caption>
|
||||
|
@ -1141,17 +1143,19 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the <code>linePoints</code>. Must be specified in
|
||||
* order for the entity to render.
|
||||
* @property {Vec3[]} strokeColors=[]] - The base colors of each point, with values in the range <code>0.0,0.0,0.0</code>
|
||||
* — <code>1.0,1.0,1.0</code>. These colors are multiplied with the color of the texture. If there are more line
|
||||
* – <code>1.0,1.0,1.0</code>. These colors are multiplied with the color of the texture. If there are more line
|
||||
* points than stroke colors, the <code>color</code> property value is used for the remaining points.
|
||||
* <p><strong>Warning:</strong> The ordinate values are in the range <code>0.0</code> — <code>1.0</code>.</p>
|
||||
* <p><strong>Warning:</strong> The ordinate values are in the range <code>0.0</code> – <code>1.0</code>.</p>
|
||||
* @property {Color} color=255,255,255 - Used as the color for each point if <code>strokeColors</code> doesn't have a value for
|
||||
* the point.
|
||||
* @property {string} textures="" - The URL of a JPG or PNG texture to use for the lines. If you want transparency, use PNG
|
||||
* format.
|
||||
* @property {boolean} isUVModeStretch=true - If <code>true</code>, the texture is stretched to fill the whole line, otherwise
|
||||
* the texture repeats along the line.
|
||||
* @property {boolean} glow=false - If <code>true</code>, the opacity of the strokes drops off away from the line center.
|
||||
* @property {boolean} faceCamera=false - If <code>true</code>, each line segment rotates to face the camera.
|
||||
* @property {boolean} isUVModeStretch=true - <code>true</code> if the texture is stretched to fill the whole line,
|
||||
* <code>false</code> if the texture repeats along the line.
|
||||
* @property {boolean} glow=false - <code>true</code> if the opacity of the strokes drops off away from the line center,
|
||||
* <code>false</code> if it doesn't.
|
||||
* @property {boolean} faceCamera=false - <code>true</code> if each line segment rotates to face the camera, <code>false</code>
|
||||
* if they don't.
|
||||
* @example <caption>Draw a textured "V".</caption>
|
||||
* var entity = Entities.addEntity({
|
||||
* type: "PolyLine",
|
||||
|
@ -1192,9 +1196,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox
|
||||
* entity update it.<br />
|
||||
* The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how
|
||||
* the particular entity's voxels compress. Because this property value has to fit within a High Fidelity datagram packet
|
||||
* there is a limit to the size and complexity of a PolyVox entity, and edits which would result in an overflow are
|
||||
* rejected.
|
||||
* the particular entity's voxels compress. Because this property value has to fit within a High Fidelity datagram packet,
|
||||
* there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.
|
||||
* @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how
|
||||
* neighboring PolyVox entities are joined.
|
||||
* @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis.
|
||||
|
@ -1282,12 +1285,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} topMargin=0.0 - The top margin, in meters.
|
||||
* @property {number} bottomMargin=0.0 - The bottom margin, in meters.
|
||||
* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera.
|
||||
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, otherwise
|
||||
* <code>false</code>. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* <code>"none"</code>.
|
||||
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, <code>false</code>
|
||||
* if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to <code>"none"</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {boolean} isFacingAvatar - <code>true</code> if <code>billboardMode</code> is <code>"full"</code>, otherwise
|
||||
* <code>false</code>. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* @property {boolean} isFacingAvatar - <code>true</code> if <code>billboardMode</code> is <code>"full"</code>,
|
||||
* <code>false</code> if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* <code>"none"</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @example <caption>Create a text entity.</caption>
|
||||
|
@ -1312,17 +1314,18 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity.
|
||||
* @property {string} sourceUrl="" - The URL of the web page to display. This value does not change as you or others navigate
|
||||
* on the Web entity.
|
||||
* @property {Color} color=255,255,255 - The color of the web surface.
|
||||
* @property {Color} color=255,255,255 - The color of the web surface. This color tints the web page displayed: the pixel
|
||||
* colors on the web page are multiplied by the property color. For example, a value of
|
||||
* <code>{ red: 255, green: 0, blue: 0 }</code> lets only the red channel of pixels' colors through.
|
||||
* @property {number} alpha=1 - The opacity of the web surface.
|
||||
* @property {Entities.Pulse} pulse - Color and alpha pulse.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera.
|
||||
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, otherwise
|
||||
* <code>false</code>. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* <code>"none"</code>.
|
||||
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, <code>false</code>
|
||||
* if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to <code>"none"</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {boolean} isFacingAvatar - <code>true</code> if <code>billboardMode</code> is <code>"full"</code>, otherwise
|
||||
* <code>false</code>. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* @property {boolean} isFacingAvatar - <code>true</code> if <code>billboardMode</code> is <code>"full"</code>,
|
||||
* <code>false</code> if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* <code>"none"</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter
|
||||
|
@ -1331,8 +1334,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {string} scriptURL="" - The URL of a JavaScript file to inject into the web page.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the web content, in frames/second.
|
||||
* @property {WebInputMode} inputMode="touch" - The user input mode to use.
|
||||
* @property {boolean} showKeyboardFocusHighlight=true - <code>true</code> to highlight the entity when it has keyboard focus,
|
||||
* <code>false</code> to not display the highlight.
|
||||
* @property {boolean} showKeyboardFocusHighlight=true - <code>true</code> if the entity is highlighted when it has keyboard
|
||||
* focus, <code>false</code> if it isn't.
|
||||
* @example <caption>Create a Web entity displaying at 1920 x 1080 resolution.</caption>
|
||||
* var METERS_TO_INCHES = 39.3701;
|
||||
* var entity = Entities.addEntity({
|
||||
|
@ -1380,12 +1383,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Entities.ComponentMode} bloomMode="inherit" - Configures the bloom in the zone.
|
||||
* @property {Entities.Bloom} bloom - The bloom properties of the zone.
|
||||
*
|
||||
* @property {boolean} flyingAllowed=true - If <code>true</code> then visitors can fly in the zone; otherwise, they cannot.
|
||||
* Only works for domain entities.
|
||||
* @property {boolean} ghostingAllowed=true - If <code>true</code> then visitors with avatar collisions turned off will not
|
||||
* collide with content in the zone; otherwise, visitors will always collide with content in the zone. Only works for domain
|
||||
* entities.
|
||||
|
||||
* @property {boolean} flyingAllowed=true - <code>true</code> if visitors can fly in the zone; <code>false</code> if they
|
||||
* cannot. Only works for domain entities.
|
||||
* @property {boolean} ghostingAllowed=true - <code>true</code> if visitors with avatar collisions turned off will not
|
||||
* collide with content in the zone; <code>false</code> if visitors will always collide with content in the zone. Only
|
||||
* works for domain entities.
|
||||
*
|
||||
* @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the
|
||||
* zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to
|
||||
* certain properties:
|
||||
|
@ -1421,7 +1424,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @typedef {object} Entities.EntityProperties-Image
|
||||
* @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity.
|
||||
* @property {string} imageURL="" - The URL of the image to use.
|
||||
* @property {boolean} emissive=false - <code>true</code> fi the the image should be emissive (unlit), <code>false</code> if it
|
||||
* @property {boolean} emissive=false - <code>true</code> if the image should be emissive (unlit), <code>false</code> if it
|
||||
* shouldn't.
|
||||
* @property {boolean} keepAspectRatio=true - <code>true</code> if the image should maintain its aspect ratio,
|
||||
* <code>false</code> if it shouldn't.
|
||||
|
@ -1432,12 +1435,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Entities.Pulse} pulse - Color and alpha pulse.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera.
|
||||
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, otherwise
|
||||
* <code>false</code>. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* <code>"none"</code>.
|
||||
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, <code>false</code>
|
||||
* if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to <code>"none"</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {boolean} isFacingAvatar - <code>true</code> if <code>billboardMode</code> is <code>"full"</code>, otherwise
|
||||
* <code>false</code>. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* @property {boolean} isFacingAvatar - <code>true</code> if <code>billboardMode</code> is <code>"full"</code>,
|
||||
* <code>false</code> if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to
|
||||
* <code>"none"</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @example <caption>Create an image entity.</caption>
|
||||
|
@ -1461,8 +1463,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} alpha=1 - The opacity of the grid.
|
||||
* @property {Entities.Pulse} pulse - Color and alpha pulse.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {boolean} followCamera=true - If <code>true</code>, the grid is always visible even as the camera moves to another
|
||||
* position.
|
||||
* @property {boolean} followCamera=true - <code>true</code> if the grid is always visible even as the camera moves to another
|
||||
* position, <code>false</code> if it doesn't follow the camrmea.
|
||||
* @property {number} majorGridEvery=5 - Integer number of <code>minorGridEvery</code> intervals at which to draw a thick grid
|
||||
* line. Minimum value = <code>1</code>.
|
||||
* @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value =
|
||||
|
@ -1895,7 +1897,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
* @property {number} verticesCount - The number of vertices in the entity.
|
||||
* @property {number} texturesCount - The number of textures in the entity.
|
||||
* @property {number} texturesSize - The total size of the textures in the entity, in bytes.
|
||||
* @property {boolean} hasTransparent - Is <code>true</code> if any of the textures has transparency.
|
||||
* @property {boolean} hasTransparent - <code>true</code> if any of the textures has transparency, <code>false</code>
|
||||
* if none of them do.
|
||||
* @property {number} drawCalls - The number of draw calls required to render the entity.
|
||||
*/
|
||||
// currently only supported by models
|
||||
|
|
|
@ -59,8 +59,8 @@ private:
|
|||
/**jsdoc
|
||||
* The result of a {@link Entities.findRayIntersection|findRayIntersection} search using a {@link PickRay}.
|
||||
* @typedef {object} Entities.RayToEntityIntersectionResult
|
||||
* @property {boolean} intersects - <code>true</code> if the {@link PickRay} intersected an entity, otherwise
|
||||
* <code>false</code>.
|
||||
* @property {boolean} intersects - <code>true</code> if the {@link PickRay} intersected an entity, <code>false</code> if it
|
||||
* didn't.
|
||||
* @property {boolean} accurate - Is always <code>true</code>.
|
||||
* @property {Uuid} entityID - The ID if the entity intersected, if any, otherwise <code>null</code>.
|
||||
* @property {number} distance - The distance from the {@link PickRay} origin to the intersection point.
|
||||
|
@ -131,8 +131,8 @@ public:
|
|||
* <tr><td><code>leaveEntity</code></td><td>{@link Entities.leaveEntity}</td></tr>
|
||||
* <tr><td><code>mouseDoublePressOnEntity</code></td><td>{@link Entities.mouseDoublePressOnEntity}</td></tr>
|
||||
* <tr><td><code>mouseMoveOnEntity</code></td><td>{@link Entities.mouseMoveOnEntity}</td></tr>
|
||||
* <tr><td><code>mouseMoveEvent</code></td><td><span class="important">Deprecated: This is a synonym for
|
||||
* <code>mouseMoveOnEntity</code>.</span></td></tr>
|
||||
* <tr><td><code>mouseMoveEvent</code></td><td><span class="important">Deprecated: Use <code>mouseMoveOnEntity</code>
|
||||
* instead.</span></td></tr>
|
||||
* <tr><td><code>mousePressOnEntity</code></td><td>{@link Entities.mousePressOnEntity}</td></tr>
|
||||
* <tr><td><code>mouseReleaseOnEntity</code></td><td>{@link Entities.mouseReleaseOnEntity}</td></tr>
|
||||
* </tbody>
|
||||
|
@ -148,8 +148,8 @@ public:
|
|||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {Uuid} keyboardFocusEntity - The {@link Entities.EntityProperties-Web|Web} entity that has keyboard focus. If no
|
||||
* Web entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid(0)|Uuid.NULL} to
|
||||
* clear keyboard focus.
|
||||
* Web entity has keyboard focus, returns <code>null</code>; set to <code>null</code> or {@link Uuid(0)|Uuid.NULL} to clear
|
||||
* keyboard focus.
|
||||
*/
|
||||
/// handles scripting of Entity commands from JS passed to assigned clients
|
||||
class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency {
|
||||
|
@ -191,8 +191,8 @@ public:
|
|||
* objects (e.g., the <code>"keyLight"</code> property), use the property and subproperty names in dot notation (e.g.,
|
||||
* <code>"keyLight.color"</code>).
|
||||
* @returns {Entities.EntityProperties[]} The specified properties of each entity for each entity that can be found. If
|
||||
* none of the entities can be found then an empty array. If no properties are specified, then all properties are
|
||||
* returned.
|
||||
* none of the entities can be found, then an empty array is returned. If no properties are specified, then all
|
||||
* properties are returned.
|
||||
* @example <caption>Retrieve the names of the nearby entities</caption>
|
||||
* var SEARCH_RADIUS = 50; // meters
|
||||
* var entityIDs = Entities.findEntities(MyAvatar.position, SEARCH_RADIUS);
|
||||
|
@ -353,9 +353,10 @@ public slots:
|
|||
bool collisionless, bool grabbable, const glm::vec3& position, const glm::vec3& gravity);
|
||||
|
||||
/**jsdoc
|
||||
* Creates a clone of an entity. The clone has a modified <code>name</code> property, other properties set per the original
|
||||
* entity's clone-related {@link Entities.EntityProperties|properties} (e.g., <code>cloneLifetime</code>), and
|
||||
* clone-related properties set to defaults.
|
||||
* Creates a clone of an entity. The clone has the same properties as the original except that: it has a modified
|
||||
* <code>name</code> property, clone-related properties are set per the original entity's clone-related
|
||||
* {@link Entities.EntityProperties|properties} (e.g., <code>cloneLifetime</code>), and its clone-related properties are
|
||||
* set to their defaults.
|
||||
* <p>Domain entities must have their <code>cloneable</code> property value be <code>true</code> in order to be cloned. A
|
||||
* domain entity can be cloned by a client that doesn't have rez permissions in the domain.</p>
|
||||
* <p>Avatar entities must have their <code>cloneable</code> and <code>cloneAvatarEntity</code> property values be
|
||||
|
@ -507,7 +508,7 @@ public slots:
|
|||
Q_INVOKABLE QObject* getEntityObject(const QUuid& id);
|
||||
|
||||
/**jsdoc
|
||||
* Checks whether an entities's assets have been loaded. For example, for an <code>Model</code> entity the result indicates
|
||||
* Checks whether an entity's assets have been loaded. For example, for an <code>Model</code> entity the result indicates
|
||||
* whether its textures have been loaded.
|
||||
* @function Entities.isLoaded
|
||||
* @param {Uuid} id - The ID of the entity to check.
|
||||
|
@ -747,7 +748,8 @@ public slots:
|
|||
* @param {string} entityName - The name of the entity to search for.
|
||||
* @param {Vec3} center - The point about which to search.
|
||||
* @param {number} radius - The radius within which to search.
|
||||
* @param {boolean} [caseSensitive=false] - If <code>true</code> then the search is case-sensitive.
|
||||
* @param {boolean} [caseSensitive=false] - <code>true</code> if the search is case-sensitive, <code>false</code> if it is
|
||||
* case-insensitive.
|
||||
* @returns {Uuid[]} An array of entity IDs that have the specified name and intersect the search sphere. The array is
|
||||
* empty if no entities could be found.
|
||||
* @example <caption>Report the number of entities with the name, "Light-Target".</caption>
|
||||
|
@ -767,12 +769,14 @@ public slots:
|
|||
* @param {boolean} [precisionPicking=false] - <code>true</code> to pick against precise meshes, <code>false</code> to pick
|
||||
* against coarse meshes. If <code>true</code> and the intersected entity is a <code>Model</code> entity, the result's
|
||||
* <code>extraInfo</code> property includes more information than it otherwise would.
|
||||
* @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities.
|
||||
* @param {Uuid[]} [entitiesToInclude=[]] - If not empty, then the search is restricted to these entities.
|
||||
* @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search.
|
||||
* @param {boolean} [visibleOnly=false] - If <code>true</code> then only entities that are
|
||||
* <code>{@link Entities.EntityProperties|visible}</code> are searched.
|
||||
* @param {boolean} [collideableOnly=false] - If <code>true</code> then only entities that are not
|
||||
* <code>{@link Entities.EntityProperties|collisionless}</code> are searched.
|
||||
* @param {boolean} [visibleOnly=false] - <code>true</code> if only entities that are
|
||||
* <code>{@link Entities.EntityProperties|visible}</code> are searched for, <code>false</code> if their visibility
|
||||
* doesn't matter.
|
||||
* @param {boolean} [collideableOnly=false] - <code>true</code> if only entities that are not
|
||||
* <code>{@link Entities.EntityProperties|collisionless}</code> are searched, <code>false</code> if their
|
||||
* collideability doesn't matter.
|
||||
* @returns {Entities.RayToEntityIntersectionResult} The result of the search for the first intersected entity.
|
||||
* @example <caption>Find the entity directly in front of your avatar.</caption>
|
||||
* var pickRay = {
|
||||
|
@ -856,7 +860,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Sets whether or not ray picks intersect the bounding box of {@link Entities.EntityProperties-Light|Light} entities. By
|
||||
* default, Light entities are not intersected. The setting lasts for the Interface session. Ray picks are done using
|
||||
* default, Light entities are not intersected. The setting lasts for the Interface session. Ray picks are performed using
|
||||
* {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
|
||||
* @function Entities.setLightsArePickable
|
||||
* @param {boolean} value - <code>true</code> to make ray picks intersect the bounding box of
|
||||
|
@ -867,7 +871,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Gets whether or not ray picks intersect the bounding box of {@link Entities.EntityProperties-Light|Light} entities. Ray
|
||||
* picks are done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
|
||||
* picks are performed using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
|
||||
* @function Entities.getLightsArePickable
|
||||
* @returns {boolean} <code>true</code> if ray picks intersect the bounding box of
|
||||
* {@link Entities.EntityProperties-Light|Light} entities, otherwise <code>false</code>.
|
||||
|
@ -877,7 +881,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Sets whether or not ray picks intersect the bounding box of {@link Entities.EntityProperties-Zone|Zone} entities. By
|
||||
* default, Zone entities are not intersected. The setting lasts for the Interface session. Ray picks are done using
|
||||
* default, Zone entities are not intersected. The setting lasts for the Interface session. Ray picks are performed using
|
||||
* {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
|
||||
* @function Entities.setZonesArePickable
|
||||
* @param {boolean} value - <code>true</code> to make ray picks intersect the bounding box of
|
||||
|
@ -888,7 +892,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Gets whether or not ray picks intersect the bounding box of {@link Entities.EntityProperties-Zone|Zone} entities. Ray
|
||||
* picks are done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
|
||||
* picks are performed using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
|
||||
* @function Entities.getZonesArePickable
|
||||
* @returns {boolean} <code>true</code> if ray picks intersect the bounding box of
|
||||
* {@link Entities.EntityProperties-Zone|Zone} entities, otherwise <code>false</code>.
|
||||
|
@ -1068,7 +1072,7 @@ public slots:
|
|||
* the dimensions of each voxel.
|
||||
* @function Entities.worldCoordsToVoxelCoords
|
||||
* @param {Uuid} entityID - The ID of the {@link Entities.EntityProperties-PolyVox|PolyVox} entity.
|
||||
* @param {Vec3} worldCoords - The world coordinates. May be outside the entity's bounding box.
|
||||
* @param {Vec3} worldCoords - The world coordinates. The value may be outside the entity's bounding box.
|
||||
* @returns {Vec3} The voxel coordinates of the <code>worldCoords</code> if the <code>entityID</code> is a
|
||||
* {@link Entities.EntityProperties-PolyVox|PolyVox} entity, otherwise {@link Vec3(0)|Vec3.ZERO}. The value may be
|
||||
* fractional and outside the entity's bounding box.
|
||||
|
@ -1083,7 +1087,7 @@ public slots:
|
|||
* <code>Vec3.ONE</code> being the dimensions of each voxel.
|
||||
* @function Entities.voxelCoordsToLocalCoords
|
||||
* @param {Uuid} entityID - The ID of the {@link Entities.EntityProperties-PolyVox|PolyVox} entity.
|
||||
* @param {Vec3} voxelCoords - The voxel coordinates. May be fractional and outside the entity's bounding box.
|
||||
* @param {Vec3} voxelCoords - The voxel coordinates. The value may be fractional and outside the entity's bounding box.
|
||||
* @returns {Vec3} The local coordinates of the <code>voxelCoords</code> if the <code>entityID</code> is a
|
||||
* {@link Entities.EntityProperties-PolyVox|PolyVox} entity, otherwise {@link Vec3(0)|Vec3.ZERO}.
|
||||
* @example <caption>Get the world dimensions of a voxel in a PolyVox entity.</caption>
|
||||
|
@ -1107,7 +1111,7 @@ public slots:
|
|||
* <code>Vec3.ONE</code> being the dimensions of each voxel.
|
||||
* @function Entities.localCoordsToVoxelCoords
|
||||
* @param {Uuid} entityID - The ID of the {@link Entities.EntityProperties-PolyVox|PolyVox} entity.
|
||||
* @param {Vec3} localCoords - The local coordinates. May be outside the entity's bounding box.
|
||||
* @param {Vec3} localCoords - The local coordinates. The value may be outside the entity's bounding box.
|
||||
* @returns {Vec3} The voxel coordinates of the <code>worldCoords</code> if the <code>entityID</code> is a
|
||||
* {@link Entities.EntityProperties-PolyVox|PolyVox} entity, otherwise {@link Vec3(0)|Vec3.ZERO}. The value may be
|
||||
* fractional and outside the entity's bounding box.
|
||||
|
@ -2105,7 +2109,7 @@ public slots:
|
|||
* @function Entities.getPropertyInfo
|
||||
* @param {string} propertyName - The name of the property to get the information for.
|
||||
* @returns {Entities.EntityPropertyInfo} The information about the property if it can be found, otherwise an empty object.
|
||||
* @example <caption>Report property info. for some properties.</caption>
|
||||
* @example <caption>Report property information for some properties.</caption>
|
||||
* print("alpha: " + JSON.stringify(Entities.getPropertyInfo("alpha")));
|
||||
* print("script: " + JSON.stringify(Entities.getPropertyInfo("script")));
|
||||
*/
|
||||
|
@ -2209,7 +2213,7 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when your ability to make changes to the asset server's assets changes.
|
||||
* @function Entities.canWriteAssetsChanged
|
||||
* @param {boolean} canWriteAssets - <code>true</code> if the script can change the <code>?</code> property of an entity,
|
||||
* @param {boolean} canWriteAssets - <code>true</code> if the script can change the asset server's assets,
|
||||
* <code>false</code> if it can't.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
|
|
@ -44,19 +44,20 @@ static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_OFFSET { glm::vec3(0.0f) };
|
|||
* Grabbing behavior is defined by the following properties:
|
||||
*
|
||||
* @typedef {object} Entities.Grab
|
||||
* @property {boolean} grabbable=true - If <code>true</code> then the entity can be grabbed.
|
||||
* @property {boolean} grabKinematic=true - If <code>true</code> then the entity will be updated in a kinematic manner when
|
||||
* grabbed; if <code>false</code> it will be grabbed using a tractor action. A kinematic grab will make the item appear
|
||||
* @property {boolean} grabbable=true - <code>true</code> if the entity can be grabbed, <code>false</code> if it can't be.
|
||||
* @property {boolean} grabKinematic=true - <code>true</code> if the entity will be updated in a kinematic manner when
|
||||
* grabbed; <code>false</code> if it will be grabbed using a tractor action. A kinematic grab will make the item appear
|
||||
* more tightly held but will cause it to behave poorly when interacting with dynamic entities.
|
||||
* @property {boolean} grabFollowsController=true - If <code>true</code> then the entity will follow the motions of the hand
|
||||
* controller even if the avatar's hand can't get to the implied position. This should be set <code>true</code> for tools,
|
||||
* pens, etc. and <code>false</code> for things meant to decorate the hand.
|
||||
* @property {boolean} triggerable=false - If <code>true</code> then the entity will receive calls to trigger
|
||||
* {@link Controller|Controller entity methods}.
|
||||
* @property {boolean} grabDelegateToParent=true - If <code>true</code> and the entity is grabbed, the grab will be transferred
|
||||
* to its parent entity if there is one; if <code>false</code>, a child entity can be grabbed and moved relative to its
|
||||
* parent.
|
||||
* @property {boolean} equippable=true - If <code>true</code> then the entity can be equipped.
|
||||
* @property {boolean} grabFollowsController=true - <code>true</code> if the entity will follow the motions of the hand
|
||||
* controller even if the avatar's hand can't get to the implied position, <code>false</code> if it will follow the motions
|
||||
* of the avatar's hand. This should be set <code>true</code> for tools, pens, etc. and <code>false</code> for things meant
|
||||
* to decorate the hand.
|
||||
* @property {boolean} triggerable=false - <code>true</code> if the entity will receive calls to trigger
|
||||
* {@link Controller|Controller entity methods}, <code>false</code> if it won't.
|
||||
* @property {boolean} grabDelegateToParent=true - <code>true</code> if when the entity is grabbed, the grab will be
|
||||
* transferred to its parent entity if there is one; <code>false</code> if the grab won't be transferred, so a child entity
|
||||
* can be grabbed and moved relative to its parent.
|
||||
* @property {boolean} equippable=true - <code>true</code> if the entity can be equipped, <code>false</code> if it cannot.
|
||||
* @property {Vec3} equippableLeftPosition=0,0,0 - Positional offset from the left hand, when equipped.
|
||||
* @property {Quat} equippableLeftRotation=0,0,0,1 - Rotational offset from the left hand, when equipped.
|
||||
* @property {Vec3} equippableRightPosition=0,0,0 - Positional offset from the right hand, when equipped.
|
||||
|
|
|
@ -49,14 +49,16 @@ static const float INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f };
|
|||
* @property {number} hazeRange=1000 - The horizontal distance at which visibility is reduced to 95%; i.e., 95% of each pixel's
|
||||
* color is haze.
|
||||
* @property {Color} hazeColor=128,154,179 - The color of the haze when looking away from the key light.
|
||||
* @property {boolean} hazeEnableGlare=false - If <code>true</code> then the haze is colored with glare from the key light;
|
||||
* <code>hazeGlareColor</code> and <code>hazeGlareAngle</code> are used.
|
||||
* @property {boolean} hazeEnableGlare=false - <code>true</code> if the haze is colored with glare from the key light,
|
||||
* <code>false</code> if it isn't. If <code>true</code>, then <code>hazeGlareColor</code> and <code>hazeGlareAngle</code>
|
||||
* are used.
|
||||
* @property {Color} hazeGlareColor=255,299,179 - The color of the haze when looking towards the key light.
|
||||
* @property {number} hazeGlareAngle=20 - The angle in degrees across the circle around the key light that the glare color and
|
||||
* haze color are blended 50/50.
|
||||
*
|
||||
* @property {boolean} hazeAltitudeEffect=false - If <code>true</code> then haze decreases with altitude as defined by the
|
||||
* entity's local coordinate system; <code>hazeBaseRef</code> and <code>hazeCeiling</code> are used.
|
||||
* @property {boolean} hazeAltitudeEffect=false - <code>true</code> if haze decreases with altitude as defined by the
|
||||
* entity's local coordinate system, <code>false</code> if it doesn't. If <code>true</code>, then <code>hazeBaseRef</code>
|
||||
* and <code>hazeCeiling</code> are used.
|
||||
* @property {number} hazeBaseRef=0 - The y-axis value in the entity's local coordinate system at which the haze density starts
|
||||
* reducing with altitude.
|
||||
* @property {number} hazeCeiling=200 - The y-axis value in the entity's local coordinate system at which the haze density has
|
||||
|
@ -65,8 +67,8 @@ static const float INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f };
|
|||
* @property {number} hazeBackgroundBlend=0 - The proportion of the skybox image to show through the haze: <code>0.0</code>
|
||||
* displays no skybox image; <code>1.0</code> displays no haze.
|
||||
*
|
||||
* @property {boolean} hazeAttenuateKeyLight=false - If <code>true</code> then the haze attenuates the key light;
|
||||
* <code>hazeKeyLightRange</code> and <code>hazeKeyLightAltitude</code> are used.
|
||||
* @property {boolean} hazeAttenuateKeyLight=false - <code>true</code> if the haze attenuates the key light, <code>false</code>
|
||||
* if it doesn't. If <code>true</code>, then <code>hazeKeyLightRange</code> and <code>hazeKeyLightAltitude</code> are used.
|
||||
* @property {number} hazeKeyLightRange=1000 - The distance at which the haze attenuates the key light by 95%.
|
||||
* @property {number} hazeKeyLightAltitude=200 - The altitude at which the haze starts attenuating the key light (i.e., the
|
||||
* altitude at which the distance starts being calculated).
|
||||
|
|
|
@ -33,8 +33,9 @@ class ReadBitstreamToTreeParams;
|
|||
* @property {Color} color=255,255,255 - The color of the light.
|
||||
* @property {number} intensity=1 - The intensity of the light.
|
||||
* @property {Vec3} direction=0,-1,0 - The direction the light is shining.
|
||||
* @property {boolean} castShadows=false - If <code>true</code> then shadows are cast. Shadows are cast by avatars, plus
|
||||
* {@link Entities.EntityProperties-Model|Model} and {@link Entities.EntityProperties-Shape|Shape} entities that have their
|
||||
* @property {boolean} castShadows=false - <code>true</code> if shadows are cast, <code>false</code> if they aren't. Shadows
|
||||
* are cast by avatars, plus {@link Entities.EntityProperties-Model|Model} and
|
||||
* {@link Entities.EntityProperties-Shape|Shape} entities that have their
|
||||
* <code>{@link Entities.EntityProperties|canCastShadow}</code> property set to <code>true</code>.
|
||||
*/
|
||||
class KeyLightPropertyGroup : public PropertyGroup {
|
||||
|
|
|
@ -115,15 +115,15 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
|
|||
* Supported models are: <code>"hifi_pbr"</code>.
|
||||
* @property {string} name="" - A name for the material. Supported by all material models.
|
||||
* @property {ColorFloat|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A
|
||||
* {@link ColorFloat} value is treated as sRGB and must have component values in the range <code>0.0</code> —
|
||||
* {@link ColorFloat} value is treated as sRGB and must have component values in the range <code>0.0</code> –
|
||||
* <code>1.0</code>. A {@link RGBS} value can be either RGB or sRGB.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {number|string} opacity=1.0 - The opacity, range <code>0.0</code> – <code>1.0</code>.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {boolean|string} unlit=false - If <code>true</code>, the material is not lit, otherwise it is.
|
||||
* @property {boolean|string} unlit=false - <code>true</code> if the material is not lit, <code>false</code> if it is.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {ColorFloat|RGBS|string} albedo - The albedo color. A {@link ColorFloat} value is treated as sRGB and must have
|
||||
* component values in the range <code>0.0</code> — <code>1.0</code>. A {@link RGBS} value can be either RGB or sRGB.
|
||||
* component values in the range <code>0.0</code> – <code>1.0</code>. A {@link RGBS} value can be either RGB or sRGB.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* @property {number|string} roughness - The roughness, range <code>0.0</code> – <code>1.0</code>.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
|
@ -172,9 +172,9 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
|
|||
* @property {string} materialParams - Parameters for controlling the material projection and repetition.
|
||||
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
|
||||
* <p><em>Currently not used.</em></p>
|
||||
* @property {boolean} defaultFallthrough=false - If <code>true</code>, all properties fall through to the material below
|
||||
* unless they are set. If <code>false</code>, they respect their individual fall-through setting. <code>"hifi_pbr"</code>
|
||||
* model only.
|
||||
* @property {boolean} defaultFallthrough=false - <code>true</code> if all properties fall through to the material below
|
||||
* unless they are set, <code>false</code> if properties respect their individual fall-through settings.
|
||||
* <code>"hifi_pbr"</code> model only.
|
||||
*/
|
||||
// Note: See MaterialEntityItem.h for default values used in practice.
|
||||
std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) {
|
||||
|
|
|
@ -1088,12 +1088,14 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packe
|
|||
if (parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) {
|
||||
|
||||
if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) {
|
||||
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
|
||||
|
||||
qCDebug(networking, "New public socket received from STUN server is %s:%hu",
|
||||
qCDebug(networking, "New public socket received from STUN server is %s:%hu (was %s:%hu)",
|
||||
newPublicAddress.toString().toStdString().c_str(),
|
||||
newPublicPort,
|
||||
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
|
||||
_publicSockAddr.getPort());
|
||||
|
||||
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
|
||||
|
||||
if (!_hasCompletedInitialSTUN) {
|
||||
// if we're here we have definitely completed our initial STUN sequence
|
||||
stopInitialSTUNUpdate(true);
|
||||
|
|
|
@ -59,13 +59,13 @@ void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) {
|
|||
|
||||
bool wasOldSocketNull = _publicSocket.isNull();
|
||||
|
||||
auto temp = _publicSocket.objectName();
|
||||
auto previousSocket = _publicSocket;
|
||||
_publicSocket = publicSocket;
|
||||
_publicSocket.setObjectName(temp);
|
||||
_publicSocket.setObjectName(previousSocket.objectName());
|
||||
|
||||
if (!wasOldSocketNull) {
|
||||
qCDebug(networking) << "Public socket change for node" << *this;
|
||||
emit socketUpdated();
|
||||
qCDebug(networking) << "Public socket change for node" << *this << "; previously" << previousSocket;
|
||||
emit socketUpdated(previousSocket, _publicSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,13 +79,13 @@ void NetworkPeer::setLocalSocket(const HifiSockAddr& localSocket) {
|
|||
|
||||
bool wasOldSocketNull = _localSocket.isNull();
|
||||
|
||||
auto temp = _localSocket.objectName();
|
||||
auto previousSocket = _localSocket;
|
||||
_localSocket = localSocket;
|
||||
_localSocket.setObjectName(temp);
|
||||
_localSocket.setObjectName(previousSocket.objectName());
|
||||
|
||||
if (!wasOldSocketNull) {
|
||||
qCDebug(networking) << "Local socket change for node" << *this;
|
||||
emit socketUpdated();
|
||||
qCDebug(networking) << "Local socket change for node" << *this << "; previously" << previousSocket;
|
||||
emit socketUpdated(previousSocket, _localSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,13 +99,13 @@ void NetworkPeer::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
|
|||
|
||||
bool wasOldSocketNull = _symmetricSocket.isNull();
|
||||
|
||||
auto temp = _symmetricSocket.objectName();
|
||||
auto previousSocket = _symmetricSocket;
|
||||
_symmetricSocket = symmetricSocket;
|
||||
_symmetricSocket.setObjectName(temp);
|
||||
_symmetricSocket.setObjectName(previousSocket.objectName());
|
||||
|
||||
if (!wasOldSocketNull) {
|
||||
qCDebug(networking) << "Symmetric socket change for node" << *this;
|
||||
emit socketUpdated();
|
||||
qCDebug(networking) << "Symmetric socket change for node" << *this << "; previously" << previousSocket;
|
||||
emit socketUpdated(previousSocket, _symmetricSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ public slots:
|
|||
signals:
|
||||
void pingTimerTimeout();
|
||||
void socketActivated(const HifiSockAddr& sockAddr);
|
||||
void socketUpdated();
|
||||
void socketUpdated(HifiSockAddr previousAddress, HifiSockAddr currentAddress);
|
||||
|
||||
protected:
|
||||
void setActiveSocket(HifiSockAddr* discoveredSocket);
|
||||
|
|
|
@ -114,6 +114,7 @@ SendQueue& Connection::getSendQueue() {
|
|||
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
|
||||
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
||||
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout);
|
||||
QObject::connect(this, &Connection::destinationAddressChange, _sendQueue.get(), &SendQueue::updateDestinationAddress);
|
||||
|
||||
|
||||
// set defaults on the send queue from our congestion control object and estimatedTimeout()
|
||||
|
@ -485,3 +486,10 @@ std::unique_ptr<Packet> PendingReceivedMessage::removeNextPacket() {
|
|||
}
|
||||
return std::unique_ptr<Packet>();
|
||||
}
|
||||
|
||||
void Connection::setDestinationAddress(const HifiSockAddr& destination) {
|
||||
if (_destination != destination) {
|
||||
_destination = destination;
|
||||
emit destinationAddressChange(destination);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,10 +76,12 @@ public:
|
|||
|
||||
void recordSentUnreliablePackets(int wireSize, int payloadSize);
|
||||
void recordReceivedUnreliablePackets(int wireSize, int payloadSize);
|
||||
void setDestinationAddress(const HifiSockAddr& destination);
|
||||
|
||||
signals:
|
||||
void packetSent();
|
||||
void receiverHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
||||
void destinationAddressChange(HifiSockAddr currentAddress);
|
||||
|
||||
private slots:
|
||||
void recordSentPackets(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint);
|
||||
|
|
|
@ -557,3 +557,7 @@ void SendQueue::deactivate() {
|
|||
bool SendQueue::isFlowWindowFull() const {
|
||||
return seqlen(SequenceNumber { (uint32_t) _lastACKSequenceNumber }, _currentSequenceNumber) > _flowWindowSize;
|
||||
}
|
||||
|
||||
void SendQueue::updateDestinationAddress(HifiSockAddr newAddress) {
|
||||
_destination = newAddress;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ public slots:
|
|||
void ack(SequenceNumber ack);
|
||||
void fastRetransmit(SequenceNumber ack);
|
||||
void handshakeACK();
|
||||
void updateDestinationAddress(HifiSockAddr newAddress);
|
||||
|
||||
signals:
|
||||
void packetSent(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint);
|
||||
|
|
|
@ -538,6 +538,33 @@ void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
|
|||
}
|
||||
}
|
||||
|
||||
void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAddr currentAddress) {
|
||||
{
|
||||
Lock connectionsLock(_connectionsHashMutex);
|
||||
_connectionsHash.erase(currentAddress);
|
||||
|
||||
const auto connectionIter = _connectionsHash.find(previousAddress);
|
||||
if (connectionIter != _connectionsHash.end()) {
|
||||
auto connection = move(connectionIter->second);
|
||||
_connectionsHash.erase(connectionIter);
|
||||
connection->setDestinationAddress(currentAddress);
|
||||
_connectionsHash[currentAddress] = move(connection);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex);
|
||||
_unreliableSequenceNumbers.erase(currentAddress);
|
||||
|
||||
const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress);
|
||||
if (sequenceNumbersIter != _unreliableSequenceNumbers.end()) {
|
||||
auto sequenceNumbers = sequenceNumbersIter->second;
|
||||
_unreliableSequenceNumbers.erase(sequenceNumbersIter);
|
||||
_unreliableSequenceNumbers[currentAddress] = sequenceNumbers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
|
||||
void Socket::sendFakedHandshakeRequest(const HifiSockAddr& sockAddr) {
|
||||
|
|
|
@ -106,11 +106,11 @@ private slots:
|
|||
|
||||
void handleSocketError(QAbstractSocket::SocketError socketError);
|
||||
void handleStateChanged(QAbstractSocket::SocketState socketState);
|
||||
void handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAddr currentAddress);
|
||||
|
||||
private:
|
||||
void setSystemBufferSizes();
|
||||
Connection* findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreation = false);
|
||||
bool socketMatchesNodeOrDomain(const HifiSockAddr& sockAddr);
|
||||
|
||||
// privatized methods used by UDTTest - they are private since they must be called on the Socket thread
|
||||
ConnectionStats::Stats sampleStatsForConnection(const HifiSockAddr& destination);
|
||||
|
|
|
@ -143,8 +143,8 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* The <code>"offset"</code> {@link Entities.ActionType|ActionType} moves an entity so that it is a set distance away from a
|
||||
* target point.
|
||||
* The <code>"offset"</code> {@link Entities.ActionType|ActionType} moves an entity so that it is a defined distance away from
|
||||
* a target point.
|
||||
* It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}:
|
||||
*
|
||||
* @typedef {object} Entities.ActionArguments-Offset
|
||||
|
|
|
@ -95,23 +95,24 @@ bool ObjectDynamic::updateArguments(QVariantMap arguments) {
|
|||
|
||||
/**jsdoc
|
||||
* Different entity action types have different arguments: some common to all actions (listed in the table) and some specific
|
||||
* to each {@link Entities.ActionType|ActionType} (linked to below). The arguments are accessed as an object of property names
|
||||
* and values.
|
||||
* to each {@link Entities.ActionType|ActionType} (linked to below).
|
||||
*
|
||||
* @typedef {object} Entities.ActionArguments
|
||||
* @property {Entities.ActionType} type - The type of action.
|
||||
* @property {string} tag="" - A string that a script can use for its own purposes.
|
||||
* @property {number} ttl=0 - How long the action should exist, in seconds, before it is automatically deleted. A value of
|
||||
* <code>0</code> means that the action should not be deleted.
|
||||
* @property {boolean} isMine=true - Is <code>true</code> if the action was created during the current client session,
|
||||
* <code>false</code> otherwise. <em>Read-only.</em>
|
||||
* @property {boolean} ::no-motion-state - Is present when the entity hasn't been registered with the physics engine yet (e.g.,
|
||||
* if the action hasn't been properly configured), otherwise <code>undefined</code>. <em>Read-only.</em>
|
||||
* @property {boolean} ::active - Is <code>true</code> when the action is modifying the entity's motion, <code>false</code>
|
||||
* otherwise. Is present once the entity has been registered with the physics engine, otherwise <code>undefined</code>.
|
||||
* @property {boolean} isMine=true - <code>true</code> if the action was created during the current client session,
|
||||
* <code>false</code> if it wasn't. <em>Read-only.</em>
|
||||
* @property {boolean} ::no-motion-state - Is present with a value of <code>true</code> when the entity hasn't been registered
|
||||
* with the physics engine yet (e.g., if the action hasn't been properly configured), otherwise the property is
|
||||
* <code>undefined</code>. <em>Read-only.</em>
|
||||
* @property {boolean} ::active - <code>true</code> when the action is modifying the entity's motion, <code>false</code>
|
||||
* otherwise. Is present once the entity has been registered with the physics engine, otherwise the property is
|
||||
* <code>undefined</code>.
|
||||
* <em>Read-only.</em>
|
||||
* @property {Entities.PhysicsMotionType} ::motion-type - How the entity moves with the action. Is present once the entity has
|
||||
* been registered with the physics engine, otherwise <code>undefined</code>. <em>Read-only.</em>
|
||||
* been registered with the physics engine, otherwise the property is <code>undefined</code>. <em>Read-only.</em>
|
||||
*
|
||||
* @comment The different action types have additional arguments as follows:
|
||||
* @see {@link Entities.ActionArguments-FarGrab|ActionArguments-FarGrab}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
#include "Logging.h"
|
||||
#include "impl/SharedObject.h"
|
||||
|
@ -33,6 +34,23 @@
|
|||
using namespace hifi::qml;
|
||||
using namespace hifi::qml::impl;
|
||||
|
||||
QmlUrlValidator OffscreenSurface::validator = [](const QUrl& url) -> bool {
|
||||
if (url.isRelative()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (url.isLocalFile()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (url.scheme() == URL_SCHEME_QRC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// By default, only allow local QML, either from the local filesystem or baked into the QRC
|
||||
return false;
|
||||
};
|
||||
|
||||
static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) {
|
||||
return glm::clamp(size, glm::uvec2(1), glm::uvec2(maxDimension));
|
||||
}
|
||||
|
@ -307,6 +325,10 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource,
|
|||
// For desktop toolbar mode window: stop script when window is closed.
|
||||
if (qmlSource.isEmpty()) {
|
||||
getSurfaceContext()->engine()->quit();
|
||||
}
|
||||
|
||||
if (!validator(qmlSource)) {
|
||||
qCWarning(qmlLogging) << "Unauthorized QML URL found" << qmlSource;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ class SharedObject;
|
|||
|
||||
using QmlContextCallback = ::std::function<void(QQmlContext*)>;
|
||||
using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
|
||||
using QmlUrlValidator = std::function<bool(const QUrl&)>;
|
||||
|
||||
class OffscreenSurface : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -47,10 +48,13 @@ class OffscreenSurface : public QObject {
|
|||
public:
|
||||
static const QmlContextObjectCallback DEFAULT_CONTEXT_OBJECT_CALLBACK;
|
||||
static const QmlContextCallback DEFAULT_CONTEXT_CALLBACK;
|
||||
|
||||
static QmlUrlValidator validator;
|
||||
using TextureAndFence = std::pair<uint32_t, void*>;
|
||||
using MouseTranslator = std::function<QPoint(const QPointF&)>;
|
||||
|
||||
|
||||
static const QmlUrlValidator& getUrlValidator() { return validator; }
|
||||
static void setUrlValidator(const QmlUrlValidator& newValidator) { validator = newValidator; }
|
||||
static void setSharedContext(QOpenGLContext* context);
|
||||
|
||||
OffscreenSurface();
|
||||
|
|
|
@ -30,7 +30,7 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
|
|||
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM);
|
||||
|
||||
// Layered objects are not culled
|
||||
const ItemFilter layeredFilter = ItemFilter::Builder().withVisible().withoutSubMetaCulled().withTagBits(tagBits, tagMask);
|
||||
const ItemFilter layeredFilter = ItemFilter::Builder::visibleWorldItems().withTagBits(tagBits, tagMask);
|
||||
const auto nonspatialFilter = render::Varying(layeredFilter);
|
||||
const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchLayeredSelection", nonspatialFilter);
|
||||
|
||||
|
|
|
@ -108,8 +108,8 @@ public:
|
|||
* Sets whether your microphone audio is echoed back to you from the server. When enabled, microphone audio is echoed
|
||||
* only if you're unmuted or are using push-to-talk.
|
||||
* @function Audio.setServerEcho
|
||||
* @parm {boolean} serverEcho - <code>true</code> to enable echoing microphone back to you from the server,
|
||||
* <code>false<code> to disable.
|
||||
* @param {boolean} serverEcho - <code>true</code> to enable echoing microphone back to you from the server,
|
||||
* <code>false</code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void setServerEcho(bool serverEcho);
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/tablet-ui/tabletUI.js",
|
||||
"system/emote.js",
|
||||
"system/miniTablet.js",
|
||||
"system/audioMuteOverlay.js"
|
||||
"system/audioMuteOverlay.js",
|
||||
"system/keyboardShortcuts/keyboardShortcuts.js"
|
||||
];
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||
"system/controllers/controllerScripts.js"
|
||||
|
|
22
scripts/developer/utilities/render/shadow.qml
Normal file → Executable file
22
scripts/developer/utilities/render/shadow.qml
Normal file → Executable file
|
@ -67,7 +67,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 20
|
||||
spacing: 10
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: hifi.dimensions.contentMargin.x
|
||||
|
@ -109,6 +109,26 @@ Rectangle {
|
|||
font.italic: true
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Bias Input")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "biasInput"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
height: 38
|
||||
width:250
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Shadow Max Distance")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "globalMaxDistance"
|
||||
max: 100.0
|
||||
min: 1.0
|
||||
height: 38
|
||||
width:250
|
||||
}
|
||||
Repeater {
|
||||
model: [
|
||||
"0", "1", "2", "3"
|
||||
|
|
29
scripts/system/keyboardShortcuts/keyboardShortcuts.js
Normal file
29
scripts/system/keyboardShortcuts/keyboardShortcuts.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
"use strict";
|
||||
|
||||
//
|
||||
// keyboardShortcuts.js
|
||||
// scripts/system/keyboardShortcuts
|
||||
//
|
||||
// Created by Preston Bezos on 06/28/2019
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function () { // BEGIN LOCAL_SCOPE
|
||||
function keyPressEvent(event) {
|
||||
if (event.text.toUpperCase() === "B" && event.isControl) {
|
||||
Window.openWebBrowser();
|
||||
} else if (event.text.toUpperCase() === "N" && event.isControl) {
|
||||
Users.toggleIgnoreRadius();
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
Controller.keyPressEvent.disconnect(keyPressEvent);
|
||||
}
|
||||
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
}()); // END LOCAL_SCOPE
|
Loading…
Reference in a new issue