()->getPoint2DTransform(virtualPadManager.getJumpButtonPosition(),
- _virtualPadJumpBtnPixelSize, _virtualPadJumpBtnPixelSize);
- render([&](gpu::Batch& batch) {
- batch.enableStereo(false);
- batch.setProjectionTransform(mat4());
- batch.setPipeline(_cursorPipeline);
+
+ batch.setResourceTexture(0, _virtualPadStickTexture);
+ batch.setModelTransform(stickTransform);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+
+ if (!virtualPadManager.getLeftVirtualPad()->isBeingTouched()) {
batch.setResourceTexture(0, _virtualPadJumpBtnTexture);
- batch.resetViewTransform();
batch.setModelTransform(jumpTransform);
- batch.setViewportTransform(ivec4(uvec2(0), getRecommendedRenderSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
- });
- }
+ }
+ });
}
#endif
Parent::compositeExtra();
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index 4b33565bfd..3cfc0651d8 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -336,9 +336,8 @@ void OpenGLDisplayPlugin::deactivate() {
_container->showDisplayPluginsTools(false);
if (!_container->currentDisplayActions().isEmpty()) {
- auto menu = _container->getPrimaryMenu();
foreach(auto itemInfo, _container->currentDisplayActions()) {
- menu->removeMenuItem(itemInfo.first, itemInfo.second);
+ _container->removeMenuItem(itemInfo.first, itemInfo.second);
}
_container->currentDisplayActions().clear();
}
diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
index 5a3caa55fe..5062162b6e 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
@@ -251,7 +251,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
updateAmbientLightFromEntity(entity);
}
- if (skyboxChanged) {
+ if (skyboxChanged || _proceduralUserData != entity->getUserData()) {
updateKeyBackgroundFromEntity(entity);
}
@@ -295,6 +295,10 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return true;
}
+ if (entity->getUserData() != _proceduralUserData) {
+ return true;
+ }
+
#if 0
if (_typedEntity->getCompoundShapeURL() != _lastShapeURL) {
return true;
diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp
index d89dd4f9d0..6c6c4b37d0 100644
--- a/libraries/entities/src/EntityEditPacketSender.cpp
+++ b/libraries/entities/src/EntityEditPacketSender.cpp
@@ -77,7 +77,6 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type,
_myAvatar->updateAvatarEntity(entityItemID, binaryProperties);
entity->setLastBroadcast(usecTimestampNow());
- return;
}
@@ -85,10 +84,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
EntityTreePointer entityTree,
EntityItemID entityItemID,
const EntityItemProperties& properties) {
- if (!_shouldSend) {
- return; // bail early
- }
-
if (properties.getClientOnly() && properties.getOwningAvatarID() == _myAvatar->getID()) {
// this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server
queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties);
@@ -147,9 +142,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
}
void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) {
- if (!_shouldSend) {
- return; // bail early
- }
// in case this was a clientOnly entity:
if(_myAvatar) {
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 1d81a6ae6d..d35c01a51b 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -1070,7 +1070,7 @@ bool EntityItem::stepKinematicMotion(float timeElapsed) {
const float MAX_TIME_ELAPSED = 1.0f; // seconds
if (timeElapsed > MAX_TIME_ELAPSED) {
- qCWarning(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
+ qCDebug(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
}
timeElapsed = glm::min(timeElapsed, MAX_TIME_ELAPSED);
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index 8adb5138f2..7e47d9e2d4 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -481,8 +481,8 @@ public slots:
/**jsdoc
* Gets the status of server entity script attached to an entity
* @function Entities.getServerScriptStatus
- * @property {Uuid} entityID - The ID of the entity to get the server entity script status for.
- * @property {Entities~getServerScriptStatusCallback} callback - The function to call upon completion.
+ * @param {Uuid} entityID - The ID of the entity to get the server entity script status for.
+ * @param {Entities~getServerScriptStatusCallback} callback - The function to call upon completion.
* @returns {boolean} true
always.
*/
/**jsdoc
diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp
index 91f7954943..6598a26c99 100644
--- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp
+++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp
@@ -103,12 +103,15 @@ void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage)
bool OffscreenGLCanvas::makeCurrent() {
bool result = _context->makeCurrent(_offscreenSurface);
- std::call_once(_reportOnce, []{
- qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
- qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
- qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
- qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
- });
+ if (glGetString) {
+ std::call_once(_reportOnce, [] {
+ qCDebug(glLogging) << "GL Version: " << QString((const char*)glGetString(GL_VERSION));
+ qCDebug(glLogging) << "GL Shader Language Version: "
+ << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
+ qCDebug(glLogging) << "GL Vendor: " << QString((const char*)glGetString(GL_VENDOR));
+ qCDebug(glLogging) << "GL Renderer: " << QString((const char*)glGetString(GL_RENDERER));
+ });
+ }
return result;
}
diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp
index 151b5bd1f9..64c9033cf7 100644
--- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp
+++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp
@@ -17,7 +17,6 @@ std::string GL41Backend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 410 core
#define GPU_GL410
- #define PRECISIONQ
#define BITFIELD int
)SHADER");
return header;
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp
index 6f6ded518f..44e439df55 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp
@@ -18,7 +18,6 @@ std::string GL45Backend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 450 core
#define GPU_GL450
- #define PRECISIONQ
#define BITFIELD int
)SHADER"
#ifdef GPU_SSBO_TRANSFORM_OBJECT
diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp
index 4278d732c8..7e8056ba79 100644
--- a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp
+++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp
@@ -17,10 +17,9 @@ std::string GLESBackend::getBackendShaderHeader() const {
static const std::string header(
R"SHADER(#version 310 es
#extension GL_EXT_texture_buffer : enable
- precision lowp float; // check precision 2
- precision lowp samplerBuffer;
- precision lowp sampler2DShadow;
- #define PRECISIONQ highp
+ precision highp float;
+ precision highp samplerBuffer;
+ precision highp sampler2DShadow;
#define BITFIELD highp int
)SHADER");
return header;
diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h
index d4e4a89636..7a3e0a2f82 100644
--- a/libraries/gpu/src/gpu/Format.h
+++ b/libraries/gpu/src/gpu/Format.h
@@ -50,47 +50,50 @@ enum Type : uint8_t {
};
// Array providing the size in bytes for a given scalar type
static const int TYPE_SIZE[NUM_TYPES] = {
- 4,
- 4,
- 4,
- 2,
- 2,
- 2,
- 1,
- 1,
+ 4, // FLOAT
+ 4, // INT32
+ 4, // UINT32
+ 2, // HALF
+ 2, // INT16
+ 2, // UINT16
+ 1, // INT8
+ 1, // UINT8
// normalized values
- 4,
- 4,
- 2,
- 2,
- 1,
- 1,
- 4,
+ 4, // NINT32
+ 4, // NUINT32
+ 2, // NINT16
+ 2, // NUINT16
+ 1, // NINT8
+ 1, // NUINT8
+ 1, // NUINT2
+ 1, // NINT2_10_10_10
- 1
+ 1, // COMPRESSED
};
+
// Array answering the question Does this type is integer or not
static const bool TYPE_IS_INTEGER[NUM_TYPES] = {
- false,
- true,
- true,
- false,
- true,
- true,
- true,
- true,
+ false, // FLOAT
+ true, // INT32
+ true, // UINT32
+ false, // HALF
+ true, // INT16
+ true, // UINT16
+ true, // INT8
+ true, // UINT8
// Normalized values
- false,
- false,
- false,
- false,
- false,
- false,
- false,
+ false, // NINT32
+ false, // NUINT32
+ false, // NINT16
+ false, // NUINT16
+ false, // NINT8
+ false, // NUINT8
+ false, // NUINT2
+ false, // NINT2_10_10_10
- false,
+ false, // COMPRESSED
};
// Dimension of an Element
@@ -367,9 +370,9 @@ public:
static const Element PART_DRAWCALL;
protected:
- uint8 _semantic;
- uint8 _dimension : 4;
- uint8 _type : 4;
+ uint16 _semantic : 7;
+ uint16 _dimension : 4;
+ uint16 _type : 5;
};
diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp
index 0822af3cfb..129c125411 100644
--- a/libraries/gpu/src/gpu/Texture_ktx.cpp
+++ b/libraries/gpu/src/gpu/Texture_ktx.cpp
@@ -46,13 +46,14 @@ struct GPUKTXPayload {
memcpy(data, &_samplerDesc, sizeof(Sampler::Desc));
data += sizeof(Sampler::Desc);
-
+
// We can't copy the bitset in Texture::Usage in a crossplateform manner
// So serialize it manually
- *(uint32*)data = _usage._flags.to_ulong();
+ uint32 usageData = _usage._flags.to_ulong();
+ memcpy(data, &usageData, sizeof(uint32));
data += sizeof(uint32);
- *(TextureUsageType*)data = _usageType;
+ memcpy(data, &_usageType, sizeof(TextureUsageType));
data += sizeof(TextureUsageType);
return data + PADDING;
@@ -77,13 +78,15 @@ struct GPUKTXPayload {
memcpy(&_samplerDesc, data, sizeof(Sampler::Desc));
data += sizeof(Sampler::Desc);
-
+
// We can't copy the bitset in Texture::Usage in a crossplateform manner
// So unserialize it manually
- _usage = Texture::Usage(*(const uint32*)data);
+ uint32 usageData;
+ memcpy(&usageData, data, sizeof(uint32));
+ _usage = Texture::Usage(usageData);
data += sizeof(uint32);
- _usageType = *(const TextureUsageType*)data;
+ memcpy(&_usageType, data, sizeof(TextureUsageType));
return true;
}
@@ -710,4 +713,4 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
return false;
}
return true;
-}
\ No newline at end of file
+}
diff --git a/libraries/graphics/src/graphics/Light.slh b/libraries/graphics/src/graphics/Light.slh
index c8992730f0..53b840f5fb 100644
--- a/libraries/graphics/src/graphics/Light.slh
+++ b/libraries/graphics/src/graphics/Light.slh
@@ -34,7 +34,7 @@ vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradi
// Light Ambient
struct LightAmbient {
- PRECISIONQ vec4 _ambient;
+ vec4 _ambient;
SphericalHarmonics _ambientSphere;
mat4 transform;
};
diff --git a/libraries/graphics/src/graphics/LightIrradiance.shared.slh b/libraries/graphics/src/graphics/LightIrradiance.shared.slh
index 4ae7967bf5..13d6cf4b93 100644
--- a/libraries/graphics/src/graphics/LightIrradiance.shared.slh
+++ b/libraries/graphics/src/graphics/LightIrradiance.shared.slh
@@ -14,9 +14,9 @@
#define LightIrradianceConstRef LightIrradiance
struct LightIrradiance {
- PRECISIONQ vec4 colorIntensity;
+ vec4 colorIntensity;
// falloffRadius, cutoffRadius, falloffSpot, spare
- PRECISIONQ vec4 attenuation;
+ vec4 attenuation;
};
diff --git a/libraries/graphics/src/graphics/LightVolume.shared.slh b/libraries/graphics/src/graphics/LightVolume.shared.slh
index 4e4359eac0..3f0cb135f5 100644
--- a/libraries/graphics/src/graphics/LightVolume.shared.slh
+++ b/libraries/graphics/src/graphics/LightVolume.shared.slh
@@ -16,8 +16,8 @@
#define LightVolumeConstRef LightVolume
struct LightVolume {
- PRECISIONQ vec4 positionRadius;
- PRECISIONQ vec4 directionSpotCos;
+ vec4 positionRadius;
+ vec4 directionSpotCos;
};
bool lightVolume_isPoint(LightVolume lv) { return bool(lv.directionSpotCos.w < 0.f); }
diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh
index ecf3c18a0e..dd2985b4da 100644
--- a/libraries/graphics/src/graphics/Material.slh
+++ b/libraries/graphics/src/graphics/Material.slh
@@ -15,10 +15,10 @@
// to what is provided by the uniform buffer, or the material key has the wrong bits
struct Material {
- PRECISIONQ vec4 _emissiveOpacity;
- PRECISIONQ vec4 _albedoRoughness;
- PRECISIONQ vec4 _fresnelMetallic;
- PRECISIONQ vec4 _scatteringSpare2Key;
+ vec4 _emissiveOpacity;
+ vec4 _albedoRoughness;
+ vec4 _fresnelMetallic;
+ vec4 _scatteringSpare2Key;
};
uniform materialBuffer {
@@ -64,7 +64,4 @@ const BITFIELD OCCLUSION_MAP_BIT = 0x00004000;
const BITFIELD LIGHTMAP_MAP_BIT = 0x00008000;
const BITFIELD SCATTERING_MAP_BIT = 0x00010000;
-#ifdef GL_ES
-precision lowp float;
-#endif
<@endif@>
diff --git a/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh b/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh
index 6e1763dcba..312824c5a3 100644
--- a/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh
+++ b/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh
@@ -16,15 +16,15 @@
#define SphericalHarmonicsConstRef SphericalHarmonics
struct SphericalHarmonics {
- PRECISIONQ vec4 L00;
- PRECISIONQ vec4 L1m1;
- PRECISIONQ vec4 L10;
- PRECISIONQ vec4 L11;
- PRECISIONQ vec4 L2m2;
- PRECISIONQ vec4 L2m1;
- PRECISIONQ vec4 L20;
- PRECISIONQ vec4 L21;
- PRECISIONQ vec4 L22;
+ vec4 L00;
+ vec4 L1m1;
+ vec4 L10;
+ vec4 L11;
+ vec4 L2m2;
+ vec4 L2m1;
+ vec4 L20;
+ vec4 L21;
+ vec4 L22;
};
vec4 sphericalHarmonics_evalSphericalLight(SphericalHarmonicsConstRef sh, vec3 direction) {
diff --git a/libraries/graphics/src/graphics/skybox.slf b/libraries/graphics/src/graphics/skybox.slf
index 7b25e36af7..153e73b9ef 100755
--- a/libraries/graphics/src/graphics/skybox.slf
+++ b/libraries/graphics/src/graphics/skybox.slf
@@ -35,8 +35,11 @@ void main(void) {
#ifdef PROCEDURAL
vec3 color = getSkyboxColor();
- // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
- color = pow(color, vec3(2.2));
+ // Protect from NaNs and negative values
+ color = mix(color, vec3(0), isnan(color));
+ color = max(color, vec3(0));
+ // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
+ color = pow(color, vec3(2.2));
_fragColor = vec4(color, 0.0);
// FIXME: scribe does not yet scrub out else statements
diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h
index 2d4712209d..08908dbaf6 100644
--- a/libraries/networking/src/DomainHandler.h
+++ b/libraries/networking/src/DomainHandler.h
@@ -97,7 +97,7 @@ public:
int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; }
void sentCheckInPacket();
- void domainListReceived() { _checkInPacketsSinceLastReply = 0; }
+ void clearPendingCheckins() { _checkInPacketsSinceLastReply = 0; }
/**jsdoc
* The reasons that you may be refused connection to a domain are defined by numeric values:
diff --git a/libraries/networking/src/HMACAuth.cpp b/libraries/networking/src/HMACAuth.cpp
new file mode 100644
index 0000000000..515af9a070
--- /dev/null
+++ b/libraries/networking/src/HMACAuth.cpp
@@ -0,0 +1,117 @@
+//
+// HMACAuth.cpp
+// libraries/networking/src
+//
+// Created by Simon Walton on 3/19/2018.
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "HMACAuth.h"
+
+#include
+#include
+
+#include
+#include "NetworkLogging.h"
+#include
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+HMACAuth::HMACAuth(AuthMethod authMethod)
+ : _hmacContext(HMAC_CTX_new())
+ , _authMethod(authMethod) { }
+
+HMACAuth::~HMACAuth()
+{
+ HMAC_CTX_free(_hmacContext);
+}
+
+#else
+
+HMACAuth::HMACAuth(AuthMethod authMethod)
+ : _hmacContext(new HMAC_CTX())
+ , _authMethod(authMethod) {
+ HMAC_CTX_init(_hmacContext);
+}
+
+HMACAuth::~HMACAuth() {
+ HMAC_CTX_cleanup(_hmacContext);
+ delete _hmacContext;
+}
+#endif
+
+bool HMACAuth::setKey(const char* keyValue, int keyLen) {
+ const EVP_MD* sslStruct = nullptr;
+
+ switch (_authMethod) {
+ case MD5:
+ sslStruct = EVP_md5();
+ break;
+
+ case SHA1:
+ sslStruct = EVP_sha1();
+ break;
+
+ case SHA224:
+ sslStruct = EVP_sha224();
+ break;
+
+ case SHA256:
+ sslStruct = EVP_sha256();
+ break;
+
+ case RIPEMD160:
+ sslStruct = EVP_ripemd160();
+ break;
+
+ default:
+ return false;
+ }
+
+ QMutexLocker lock(&_lock);
+ return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
+}
+
+bool HMACAuth::setKey(const QUuid& uidKey) {
+ const QByteArray rfcBytes(uidKey.toRfc4122());
+ return setKey(rfcBytes.constData(), rfcBytes.length());
+}
+
+bool HMACAuth::addData(const char* data, int dataLen) {
+ QMutexLocker lock(&_lock);
+ return (bool) HMAC_Update(_hmacContext, reinterpret_cast(data), dataLen);
+}
+
+HMACAuth::HMACHash HMACAuth::result() {
+ HMACHash hashValue(EVP_MAX_MD_SIZE);
+ unsigned int hashLen;
+ QMutexLocker lock(&_lock);
+
+ auto hmacResult = HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
+
+ if (hmacResult) {
+ hashValue.resize((size_t)hashLen);
+ } else {
+ // the HMAC_FINAL call failed - should not be possible to get into this state
+ qCWarning(networking) << "Error occured calling HMAC_Final";
+ assert(hmacResult);
+ }
+
+ // Clear state for possible reuse.
+ HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
+ return hashValue;
+}
+
+bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen) {
+ QMutexLocker lock(&_lock);
+ if (!addData(data, dataLen)) {
+ qCWarning(networking) << "Error occured calling HMACAuth::addData()";
+ assert(false);
+ return false;
+ }
+
+ hashResult = result();
+ return true;
+}
diff --git a/libraries/networking/src/HMACAuth.h b/libraries/networking/src/HMACAuth.h
new file mode 100644
index 0000000000..1346bdee17
--- /dev/null
+++ b/libraries/networking/src/HMACAuth.h
@@ -0,0 +1,47 @@
+//
+// HMACAuth.h
+// libraries/networking/src
+//
+// Created by Simon Walton on 3/19/2018.
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_HMACAuth_h
+#define hifi_HMACAuth_h
+
+#include
+#include
+#include
+
+class QUuid;
+
+class HMACAuth {
+public:
+ enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 };
+ using HMACHash = std::vector;
+
+ explicit HMACAuth(AuthMethod authMethod = MD5);
+ ~HMACAuth();
+
+ bool setKey(const char* keyValue, int keyLen);
+ bool setKey(const QUuid& uidKey);
+ // Calculate complete hash in one.
+ bool calculateHash(HMACHash& hashResult, const char* data, int dataLen);
+
+ // Append to data to be hashed.
+ bool addData(const char* data, int dataLen);
+ // Get the resulting hash from calls to addData().
+ // Note that only one hash may be calculated at a time for each
+ // HMACAuth instance if this interface is used.
+ HMACHash result();
+
+private:
+ QMutex _lock { QMutex::Recursive };
+ struct hmac_ctx_st* _hmacContext;
+ AuthMethod _authMethod;
+};
+
+#endif // hifi_HMACAuth_h
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 0660f02fd6..012a891698 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -36,6 +36,7 @@
#include "HifiSockAddr.h"
#include "NetworkLogging.h"
#include "udt/Packet.h"
+#include "HMACAuth.h"
static Setting::Handle LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
@@ -332,15 +333,20 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
if (verifiedPacket && !ignoreVerification) {
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
- QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
+ QByteArray expectedHash;
+ auto sourceNodeHMACAuth = sourceNode->getAuthenticateHash();
+ if (sourceNode->getAuthenticateHash()) {
+ expectedHash = NLPacket::hashForPacketAndHMAC(packet, *sourceNodeHMACAuth);
+ }
- // check if the md5 hash in the header matches the hash we would expect
- if (packetHeaderHash != expectedHash) {
+ // check if the HMAC-md5 hash in the header matches the hash we would expect
+ if (!sourceNodeHMACAuth || packetHeaderHash != expectedHash) {
static QMultiMap hashDebugSuppressMap;
if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
- qCDebug(networking) << packetHeaderHash << expectedHash;
qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID;
+ qCDebug(networking) << "Packet len:" << packet.getDataSize() << "Expected hash:" <<
+ expectedHash.toHex() << "Actual:" << packetHeaderHash.toHex();
hashDebugSuppressMap.insert(sourceID, headerType);
}
@@ -372,15 +378,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) {
_numCollectedBytes += packet.getDataSize();
}
-void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) {
+void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) {
if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
packet.writeSourceID(getSessionLocalID());
}
- if (!connectionSecret.isNull()
+ if (hmacAuth
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
- packet.writeVerificationHashGivenSecret(connectionSecret);
+ packet.writeVerificationHash(*hmacAuth);
}
}
@@ -396,17 +402,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
emit dataSent(destinationNode.getType(), packet.getDataSize());
destinationNode.recordBytesSent(packet.getDataSize());
- return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
+ return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getAuthenticateHash());
}
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret) {
+ HMACAuth* hmacAuth) {
Q_ASSERT(!packet.isPartOfMessage());
Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
"Trying to send a reliable packet unreliably.");
collectPacketStats(packet);
- fillPacketHeader(packet, connectionSecret);
+ fillPacketHeader(packet, hmacAuth);
return _nodeSocket.writePacket(packet, sockAddr);
}
@@ -419,7 +425,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
emit dataSent(destinationNode.getType(), packet->getDataSize());
destinationNode.recordBytesSent(packet->getDataSize());
- return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
+ return sendPacket(std::move(packet), *activeSocket, destinationNode.getAuthenticateHash());
} else {
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
return ERROR_SENDING_PACKET_BYTES;
@@ -427,18 +433,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
}
qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret) {
+ HMACAuth* hmacAuth) {
Q_ASSERT(!packet->isPartOfMessage());
if (packet->isReliable()) {
collectPacketStats(*packet);
- fillPacketHeader(*packet, connectionSecret);
+ fillPacketHeader(*packet, hmacAuth);
auto size = packet->getDataSize();
_nodeSocket.writePacket(std::move(packet), sockAddr);
return size;
} else {
- return sendUnreliablePacket(*packet, sockAddr, connectionSecret);
+ return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
}
}
@@ -447,13 +453,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
if (activeSocket) {
qint64 bytesSent = 0;
- auto connectionSecret = destinationNode.getConnectionSecret();
+ auto connectionHash = destinationNode.getAuthenticateHash();
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
- bytesSent += sendPacket(packetList.takeFront(), *activeSocket, connectionSecret);
+ bytesSent += sendPacket(packetList.takeFront(), *activeSocket,
+ connectionHash);
}
emit dataSent(destinationNode.getType(), bytesSent);
@@ -466,14 +473,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
}
qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret) {
+ HMACAuth* hmacAuth) {
qint64 bytesSent = 0;
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
- bytesSent += sendPacket(packetList.takeFront(), sockAddr, connectionSecret);
+ bytesSent += sendPacket(packetList.takeFront(), sockAddr, hmacAuth);
}
return bytesSent;
@@ -501,7 +508,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList,
for (std::unique_ptr& packet : packetList->_packets) {
NLPacket* nlPacket = static_cast(packet.get());
collectPacketStats(*nlPacket);
- fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
+ fillPacketHeader(*nlPacket, destinationNode.getAuthenticateHash());
}
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
@@ -524,7 +531,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
: overridenSockAddr;
- return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
+ return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getAuthenticateHash());
}
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer message, SharedNodePointer sendingNode) {
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index 3d6fd0cd91..05374bbfbb 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -138,19 +138,17 @@ public:
// use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
// either to a node (via its active socket) or to a manual sockaddr
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
- qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret = QUuid());
+ qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
// use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
qint64 sendPacket(std::unique_ptr packet, const Node& destinationNode);
- qint64 sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret = QUuid());
+ qint64 sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
// use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
// either to a node's active socket or to a manual sockaddr
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret = QUuid());
+ HMACAuth* hmacAuth = nullptr);
// use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
// or to a manual sock addr
@@ -372,7 +370,7 @@ protected:
qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
const QUuid& connectionSecret = QUuid());
void collectPacketStats(const NLPacket& packet);
- void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid());
+ void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
void setLocalSocket(const HifiSockAddr& sockAddr);
diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp
index ac3fbc966b..f946e97bf4 100644
--- a/libraries/networking/src/NLPacket.cpp
+++ b/libraries/networking/src/NLPacket.cpp
@@ -11,6 +11,8 @@
#include "NLPacket.h"
+#include "HMACAuth.h"
+
int NLPacket::localHeaderSize(PacketType type) {
bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
@@ -150,18 +152,16 @@ QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) {
return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
}
-QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) {
- QCryptographicHash hash(QCryptographicHash::Md5);
-
+QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) {
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
// add the packet payload and the connection UUID
- hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
- hash.addData(connectionSecret.toRfc4122());
-
- // return the hash
- return hash.result();
+ HMACAuth::HMACHash hashResult;
+ if (!hash.calculateHash(hashResult, packet.getData() + offset, packet.getDataSize() - offset)) {
+ return QByteArray();
+ }
+ return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
}
void NLPacket::writeTypeAndVersion() {
@@ -214,14 +214,14 @@ void NLPacket::writeSourceID(LocalID sourceID) const {
_sourceID = sourceID;
}
-void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const {
+void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const {
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
!PacketTypeEnum::getNonVerifiedPackets().contains(_type));
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID;
- QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret);
+ QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth);
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
}
diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h
index 9b07bc6581..4103f2068e 100644
--- a/libraries/networking/src/NLPacket.h
+++ b/libraries/networking/src/NLPacket.h
@@ -18,6 +18,8 @@
#include "udt/Packet.h"
+class HMACAuth;
+
class NLPacket : public udt::Packet {
Q_OBJECT
public:
@@ -69,7 +71,7 @@ public:
static LocalID sourceIDInHeader(const udt::Packet& packet);
static QByteArray verificationHashInHeader(const udt::Packet& packet);
- static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret);
+ static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash);
PacketType getType() const { return _type; }
void setType(PacketType type);
@@ -78,9 +80,9 @@ public:
void setVersion(PacketVersion version);
LocalID getSourceID() const { return _sourceID; }
-
+
void writeSourceID(LocalID sourceID) const;
- void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const;
+ void writeVerificationHash(HMACAuth& hmacAuth) const;
protected:
diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp
index 626503d8ae..5bbff103dd 100644
--- a/libraries/networking/src/Node.cpp
+++ b/libraries/networking/src/Node.cpp
@@ -86,7 +86,7 @@ NodeType_t NodeType::fromString(QString type) {
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
- const HifiSockAddr& localSocket, QObject* parent) :
+ const HifiSockAddr& localSocket, QObject* parent) :
NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type),
_pingMs(-1), // "Uninitialized"
@@ -108,6 +108,7 @@ void Node::setType(char type) {
_symmetricSocket.setObjectName(typeString);
}
+
void Node::updateClockSkewUsec(qint64 clockSkewSample) {
_clockSkewMovingPercentile.updatePercentile(clockSkewSample);
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
@@ -194,3 +195,16 @@ QDebug operator<<(QDebug debug, const Node& node) {
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
return debug.nospace();
}
+
+void Node::setConnectionSecret(const QUuid& connectionSecret) {
+ if (_connectionSecret == connectionSecret) {
+ return;
+ }
+
+ if (!_authenticateHash) {
+ _authenticateHash.reset(new HMACAuth());
+ }
+
+ _connectionSecret = connectionSecret;
+ _authenticateHash->setKey(_connectionSecret);
+}
diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h
index 93b6a649d4..bcfe525aa8 100644
--- a/libraries/networking/src/Node.h
+++ b/libraries/networking/src/Node.h
@@ -33,6 +33,7 @@
#include "SimpleMovingAverage.h"
#include "MovingPercentile.h"
#include "NodePermissions.h"
+#include "HMACAuth.h"
class Node : public NetworkPeer {
Q_OBJECT
@@ -55,7 +56,8 @@ public:
void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
const QUuid& getConnectionSecret() const { return _connectionSecret; }
- void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
+ void setConnectionSecret(const QUuid& connectionSecret);
+ HMACAuth* getAuthenticateHash() const { return _authenticateHash.get(); }
NodeData* getLinkedData() const { return _linkedData.get(); }
void setLinkedData(std::unique_ptr linkedData) { _linkedData = std::move(linkedData); }
@@ -97,6 +99,7 @@ private:
NodeType_t _type;
QUuid _connectionSecret;
+ std::unique_ptr _authenticateHash { nullptr };
std::unique_ptr _linkedData;
bool _isReplicated { false };
int _pingMs;
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 04e32f50cb..4920ea97c7 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -594,6 +594,8 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointerreadWithoutCopy(NUM_BYTES_RFC4122_UUID)));
+
+ _domainHandler.clearPendingCheckins();
sendDomainServerCheckIn();
}
@@ -605,7 +607,7 @@ void NodeList::processDomainServerList(QSharedPointer message)
}
// this is a packet from the domain server, reset the count of un-replied check-ins
- _domainHandler.domainListReceived();
+ _domainHandler.clearPendingCheckins();
// emit our signal so listeners know we just heard from the DS
emit receivedDomainServerList();
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 70880833bf..02853543ba 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -92,7 +92,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarQuery:
return static_cast(AvatarQueryVersion::ConicalFrustums);
default:
- return 20;
+ return 21;
}
}
diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp
index 0156013821..d8d3041270 100644
--- a/libraries/octree/src/OctreeEditPacketSender.cpp
+++ b/libraries/octree/src/OctreeEditPacketSender.cpp
@@ -23,7 +23,6 @@ const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::D
OctreeEditPacketSender::OctreeEditPacketSender() :
- _shouldSend(true),
_maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
_releaseQueuedMessagesPending(false)
{
@@ -146,10 +145,6 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(std::unique_ptr
}
void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr packet) {
- if (!_shouldSend) {
- return; // bail early
- }
-
assert(serversExist()); // we must have servers to be here!!
auto node = DependencyManager::get()->soloNodeOfType(getMyNodeType());
@@ -162,10 +157,6 @@ void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr packet
// NOTE: editMessage - is JUST the octcode/color and does not contain the packet header
void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray& editMessage) {
- if (!_shouldSend) {
- return; // bail early
- }
-
// If we don't have servers, then we will simply queue up all of these packets and wait till we have
// servers for processing
if (!serversExist()) {
diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h
index 8a33eb8b73..1dad5b74c7 100644
--- a/libraries/octree/src/OctreeEditPacketSender.h
+++ b/libraries/octree/src/OctreeEditPacketSender.h
@@ -41,12 +41,10 @@ public:
/// are we in sending mode. If we're not in sending mode then all packets and messages will be ignored and
/// not queued and not sent
- bool getShouldSend() const { return _shouldSend; }
/// set sending mode. By default we are set to shouldSend=TRUE and packets will be sent. If shouldSend=FALSE, then we'll
/// switch to not sending mode, and all packets and messages will be ignored, not queued, and not sent. This might be used
/// in an application like interface when all octree features are disabled.
- void setShouldSend(bool shouldSend) { _shouldSend = shouldSend; }
/// if you're running in non-threaded mode, you must call this method regularly
virtual bool process() override;
@@ -76,7 +74,6 @@ public slots:
protected:
using EditMessagePair = std::pair;
- bool _shouldSend;
void queuePacketToNode(const QUuid& nodeID, std::unique_ptr packet);
void queuePacketListToNode(const QUuid& nodeUUID, std::unique_ptr packetList);
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index a801392b66..7a0ead3e0d 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -349,7 +349,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
if (_numInactiveUpdates > 0) {
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
- if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
+ if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES || isServerlessMode()) {
// clear local ownership (stop sending updates) and let the server clear itself
_entity->clearSimulationOwnership();
return false;
@@ -731,7 +731,9 @@ void EntityMotionState::measureBodyAcceleration() {
// hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt
glm::vec3 velocity = getBodyLinearVelocityGTSigma();
- _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
+ const float MIN_DAMPING_FACTOR = 0.01f;
+ float invDampingAttenuationFactor = 1.0f / glm::max(powf(1.0f - _body->getLinearDamping(), dt), MIN_DAMPING_FACTOR);
+ _measuredAcceleration = (velocity * invDampingAttenuationFactor - _lastVelocity) * invDt;
_lastVelocity = velocity;
if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) {
// we fall in here when _lastMeasureStep is old: the body has just become active
@@ -833,3 +835,9 @@ void EntityMotionState::clearObjectVelocities() const {
}
_entity->setAcceleration(glm::vec3(0.0f));
}
+
+bool EntityMotionState::isServerlessMode() {
+ EntityTreeElementPointer element = _entity->getElement();
+ EntityTreePointer tree = element ? element->getTree() : nullptr;
+ return tree ? tree->isServerlessMode() : false;
+}
diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h
index a542c61d43..376cc5afe2 100644
--- a/libraries/physics/src/EntityMotionState.h
+++ b/libraries/physics/src/EntityMotionState.h
@@ -156,6 +156,8 @@ protected:
uint8_t _numInactiveUpdates { 1 };
uint8_t _bidPriority { 0 };
bool _serverVariablesSet { false };
+
+ bool isServerlessMode();
};
#endif // hifi_EntityMotionState_h
diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp
index a48989be33..4235bbd616 100644
--- a/libraries/physics/src/ObjectActionTractor.cpp
+++ b/libraries/physics/src/ObjectActionTractor.cpp
@@ -397,7 +397,7 @@ void ObjectActionTractor::deserialize(QByteArray serializedArguments) {
EntityDynamicType type;
dataStream >> type;
- assert(type == getType());
+ assert(type == getType() || type == DYNAMIC_TYPE_SPRING);
QUuid id;
dataStream >> id;
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 3ea3616b15..e2970d6a03 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -328,10 +328,18 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
}
void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
- motionState->initForBid();
- motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
- _bids.push_back(motionState);
- _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
+ if (getEntityTree()->isServerlessMode()) {
+ EntityItemPointer entity = motionState->getEntity();
+ auto nodeList = DependencyManager::get();
+ auto sessionID = nodeList->getSessionUUID();
+ entity->setSimulationOwner(SimulationOwner(sessionID, SCRIPT_GRAB_SIMULATION_PRIORITY));
+ _owned.push_back(motionState);
+ } else {
+ motionState->initForBid();
+ motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
+ _bids.push_back(motionState);
+ _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
+ }
}
void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) {
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index c155d5bd7f..f93605b730 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -19,7 +19,7 @@
#include
#include
#include
-
+#include
#include "ProceduralCommon_frag.h"
#include "Logging.h"
@@ -178,6 +178,8 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
return;
}
_shaderPath = shaderUrl.toLocalFile();
+ } else if (shaderUrl.scheme() == URL_SCHEME_QRC) {
+ _shaderPath = ":" + shaderUrl.path();
} else {
_networkShader = ShaderCache::instance().getShader(shaderUrl);
}
@@ -270,18 +272,24 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
// Leave this here for debugging
// qCDebug(procedural) << "FragmentShader:\n" << fragmentShaderSource.c_str();
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding(std::string("iChannel0"), 0));
+ slotBindings.insert(gpu::Shader::Binding(std::string("iChannel1"), 1));
+ slotBindings.insert(gpu::Shader::Binding(std::string("iChannel2"), 2));
+ slotBindings.insert(gpu::Shader::Binding(std::string("iChannel3"), 3));
+
_opaqueFragmentShader = gpu::Shader::createPixel(opaqueShaderSource);
_opaqueShader = gpu::Shader::createProgram(_vertexShader, _opaqueFragmentShader);
- _transparentFragmentShader = gpu::Shader::createPixel(transparentShaderSource);
- _transparentShader = gpu::Shader::createProgram(_vertexShader, _transparentFragmentShader);
+ gpu::Shader::makeProgram(*_opaqueShader, slotBindings);
- gpu::Shader::BindingSet slotBindings;
- slotBindings.insert(gpu::Shader::Binding(std::string("iChannel0"), 0));
- slotBindings.insert(gpu::Shader::Binding(std::string("iChannel1"), 1));
- slotBindings.insert(gpu::Shader::Binding(std::string("iChannel2"), 2));
- slotBindings.insert(gpu::Shader::Binding(std::string("iChannel3"), 3));
- gpu::Shader::makeProgram(*_opaqueShader, slotBindings);
- gpu::Shader::makeProgram(*_transparentShader, slotBindings);
+ if (!transparentShaderSource.empty() && transparentShaderSource != opaqueShaderSource) {
+ _transparentFragmentShader = gpu::Shader::createPixel(transparentShaderSource);
+ _transparentShader = gpu::Shader::createProgram(_vertexShader, _transparentFragmentShader);
+ gpu::Shader::makeProgram(*_transparentShader, slotBindings);
+ } else {
+ _transparentFragmentShader = _opaqueFragmentShader;
+ _transparentShader = _opaqueShader;
+ }
_opaquePipeline = gpu::Pipeline::create(_opaqueShader, _opaqueState);
_transparentPipeline = gpu::Pipeline::create(_transparentShader, _transparentState);
diff --git a/libraries/qml/src/qml/Logging.h b/libraries/qml/src/qml/Logging.h
index 487566701d..48efdd1da9 100644
--- a/libraries/qml/src/qml/Logging.h
+++ b/libraries/qml/src/qml/Logging.h
@@ -6,8 +6,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-#ifndef hifi_Controllers_Logging_h
-#define hifi_Controllers_Logging_h
+#ifndef hifi_QML_Logging_h
+#define hifi_QML_Logging_h
#include
diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh
index 6a5982f1e8..8abdb5cbf9 100644
--- a/libraries/render-utils/src/LightingModel.slh
+++ b/libraries/render-utils/src/LightingModel.slh
@@ -14,10 +14,10 @@
<@func declareLightingModel()@>
struct LightingModel {
- PRECISIONQ vec4 _UnlitEmissiveLightmapBackground;
- PRECISIONQ vec4 _ScatteringDiffuseSpecularAlbedo;
- PRECISIONQ vec4 _AmbientDirectionalPointSpot;
- PRECISIONQ vec4 _ShowContourObscuranceWireframe;
+ vec4 _UnlitEmissiveLightmapBackground;
+ vec4 _ScatteringDiffuseSpecularAlbedo;
+ vec4 _AmbientDirectionalPointSpot;
+ vec4 _ShowContourObscuranceWireframe;
};
uniform lightingModelBuffer{
diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp
index 63370109e0..b49735a53f 100755
--- a/libraries/render-utils/src/RenderForwardTask.cpp
+++ b/libraries/render-utils/src/RenderForwardTask.cpp
@@ -21,6 +21,8 @@
#include
+#include
+
#include "StencilMaskPass.h"
#include "ZoneRenderer.h"
#include "FadeEffect.h"
@@ -53,8 +55,9 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
// const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT];
const auto& metas = items.get0()[RenderFetchCullSortTask::META];
- // const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE];
- // const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE];
+ const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE];
+ const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE];
+
//const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND];
// const auto& spatialSelection = items[1];
@@ -75,6 +78,17 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
// draw a stencil mask in hidden regions of the framebuffer.
task.addJob("PrepareStencil", framebuffer);
+ // Layered Overlays
+ const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT);
+ const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT);
+ const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0);
+ const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0);
+
+ const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel, nullptr).asVarying();
+ const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel, nullptr).asVarying();
+ task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true);
+ task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false);
+
// Draw opaques forward
const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying();
task.addJob("DrawOpaques", opaqueInputs, shapePlumber);
diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp
index 69c5b3c689..fbb4bba263 100644
--- a/libraries/render-utils/src/RenderShadowTask.cpp
+++ b/libraries/render-utils/src/RenderShadowTask.cpp
@@ -149,9 +149,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
batch.setStateScissorRect(viewport);
batch.setFramebuffer(fbo);
- batch.clearFramebuffer(
- gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
- vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true);
+ batch.clearDepthFramebuffer(1.0, false);
glm::mat4 projMat;
Transform viewMat;
@@ -232,12 +230,11 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
const auto queryResolution = setupOutput.getN(2);
// Fetch and cull the items from the scene
- // Enable models to not cast shadows (otherwise, models will always cast shadows)
- static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask).withShadowCaster();
+ static const auto shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);
- const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
+ const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying();
const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput);
- const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
+ const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying();
const auto shadowItems = task.addJob("FetchShadowSelection", selectionInputs);
// Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not
@@ -261,21 +258,22 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
char jobName[64];
sprintf(jobName, "ShadowCascadeSetup%d", i);
const auto cascadeSetupOutput = task.addJob(jobName, i, _cullFunctor, tagBits, tagMask);
- const auto shadowFilter = cascadeSetupOutput.getN(0);
+ const auto shadowRenderFilter = cascadeSetupOutput.getN(0);
+ const auto shadowBoundsFilter = cascadeSetupOutput.getN(1);
auto antiFrustum = render::Varying(ViewFrustumPointer());
- cascadeFrustums[i] = cascadeSetupOutput.getN(1);
+ cascadeFrustums[i] = cascadeSetupOutput.getN(2);
if (i > 1) {
antiFrustum = cascadeFrustums[i - 2];
}
// CPU jobs: finer grained culling
- const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying();
+ const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowRenderFilter, shadowBoundsFilter, antiFrustum).asVarying();
const auto culledShadowItemsAndBounds = task.addJob("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW);
// GPU jobs: Render to shadow map
sprintf(jobName, "RenderShadowMap%d", i);
task.addJob(jobName, culledShadowItemsAndBounds, shapePlumber, i);
- task.addJob("ShadowCascadeTeardown", shadowFilter);
+ task.addJob("ShadowCascadeTeardown", shadowRenderFilter);
}
task.addJob("ShadowTeardown", setupOutput);
@@ -406,7 +404,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
const auto globalShadow = lightStage->getCurrentKeyShadow();
if (globalShadow && _cascadeIndexgetCascadeCount()) {
- output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask).withShadowCaster();
+ auto baseFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
+ // Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers)
+ output.edit1() = baseFilter;
+ // First item filter is to filter items to render in shadow map (so only keep casters)
+ output.edit0() = baseFilter.withShadowCaster();
// Set the keylight render args
auto& cascade = globalShadow->getCascade(_cascadeIndex);
@@ -419,10 +421,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
texelSize *= minTexelCount;
_cullFunctor._minSquareSize = texelSize * texelSize;
- output.edit1() = cascadeFrustum;
+ output.edit2() = cascadeFrustum;
} else {
output.edit0() = ItemFilter::Builder::nothing();
- output.edit1() = ViewFrustumPointer();
+ output.edit1() = ItemFilter::Builder::nothing();
+ output.edit2() = ViewFrustumPointer();
}
}
diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h
index 98b70c0c9f..19ffcb4234 100644
--- a/libraries/render-utils/src/RenderShadowTask.h
+++ b/libraries/render-utils/src/RenderShadowTask.h
@@ -118,7 +118,7 @@ private:
class RenderShadowCascadeSetup {
public:
- using Outputs = render::VaryingSet2;
+ using Outputs = render::VaryingSet3;
using JobModel = render::Job::ModelO;
RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) :
diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp
index f04427540a..b5819f114f 100644
--- a/libraries/render/src/render/CullTask.cpp
+++ b/libraries/render/src/render/CullTask.cpp
@@ -368,17 +368,19 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
RenderArgs* args = renderContext->args;
const auto& inShapes = inputs.get0();
- const auto& filter = inputs.get1();
- const auto& antiFrustum = inputs.get2();
+ const auto& cullFilter = inputs.get1();
+ const auto& boundsFilter = inputs.get2();
+ const auto& antiFrustum = inputs.get3();
auto& outShapes = outputs.edit0();
auto& outBounds = outputs.edit1();
outShapes.clear();
outBounds = AABox();
- if (!filter.selectsNothing()) {
+ if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) {
auto& details = args->_details.edit(_detailType);
Test test(_cullFunctor, args, details, antiFrustum);
+ auto scene = args->_scene;
for (auto& inItems : inShapes) {
auto key = inItems.first;
@@ -393,16 +395,26 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
if (antiFrustum == nullptr) {
for (auto& item : inItems.second) {
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
- outItems->second.emplace_back(item);
- outBounds += item.bound;
+ const auto shapeKey = scene->getItem(item.id).getKey();
+ if (cullFilter.test(shapeKey)) {
+ outItems->second.emplace_back(item);
+ }
+ if (boundsFilter.test(shapeKey)) {
+ outBounds += item.bound;
+ }
}
}
} else {
for (auto& item : inItems.second) {
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
- outItems->second.emplace_back(item);
- outBounds += item.bound;
- }
+ const auto shapeKey = scene->getItem(item.id).getKey();
+ if (cullFilter.test(shapeKey)) {
+ outItems->second.emplace_back(item);
+ }
+ if (boundsFilter.test(shapeKey)) {
+ outBounds += item.bound;
+ }
+ }
}
}
details._rendered += (int)outItems->second.size();
diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h
index 3c5a30de89..47abe8a960 100644
--- a/libraries/render/src/render/CullTask.h
+++ b/libraries/render/src/render/CullTask.h
@@ -110,7 +110,7 @@ namespace render {
class CullShapeBounds {
public:
- using Inputs = render::VaryingSet3;
+ using Inputs = render::VaryingSet4;
using Outputs = render::VaryingSet2;
using JobModel = Job::ModelIO;
diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h
index eb9a628ae3..7f7a3a68b0 100644
--- a/libraries/script-engine/src/AssetScriptingInterface.h
+++ b/libraries/script-engine/src/AssetScriptingInterface.h
@@ -186,36 +186,36 @@ public:
/**jsdoc
* @function Assets.deleteAsset
- * @property {} options
- * @property {} scope
- * @property {} [callback = ""]
+ * @param {} options
+ * @param {} scope
+ * @param {} [callback = ""]
*/
Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.resolveAsset
- * @property {} options
- * @property {} scope
- * @property {} [callback = ""]
+ * @param {} options
+ * @param {} scope
+ * @param {} [callback = ""]
*/
Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.decompressData
- * @property {} options
- * @property {} scope
- * @property {} [callback = ""]
+ * @param {} options
+ * @param {} scope
+ * @param {} [callback = ""]
*/
Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.compressData
- * @property {} options
- * @property {} scope
- * @property {} [callback = ""]
+ * @param {} options
+ * @param {} scope
+ * @param {} [callback = ""]
*/
Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
@@ -229,7 +229,7 @@ public:
/**jsdoc
* @function Assets.canWriteCacheValue
- * @property {string} url
+ * @param {string} url
* @returns {boolean}
*/
@@ -237,8 +237,8 @@ public:
/**jsdoc
* @function Assets.getCacheStatus
- * @property {} scope
- * @property {} [callback=undefined]
+ * @param {} scope
+ * @param {} [callback=undefined]
*/
Q_INVOKABLE void getCacheStatus(QScriptValue scope, QScriptValue callback = QScriptValue()) {
@@ -247,38 +247,38 @@ public:
/**jsdoc
* @function Assets.queryCacheMeta
- * @property {} options
- * @property {} scope
- * @property {} [callback=undefined]
+ * @param {} options
+ * @param {} scope
+ * @param {} [callback=undefined]
*/
Q_INVOKABLE void queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.loadFromCache
- * @property {} options
- * @property {} scope
- * @property {} [callback=undefined]
+ * @param {} options
+ * @param {} scope
+ * @param {} [callback=undefined]
*/
Q_INVOKABLE void loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.saveToCache
- * @property {} options
- * @property {} scope
- * @property {} [callback=undefined]
+ * @param {} options
+ * @param {} scope
+ * @param {} [callback=undefined]
*/
Q_INVOKABLE void saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
/**jsdoc
* @function Assets.saveToCache
- * @property {} url
- * @property {} data
- * @property {} metadata
- * @property {} scope
- * @property {} [callback=undefined]
+ * @param {} url
+ * @param {} data
+ * @param {} metadata
+ * @param {} scope
+ * @param {} [callback=undefined]
*/
Q_INVOKABLE void saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata,
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 9a383454d4..23ffbabe77 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -101,8 +101,6 @@ int functionSignatureMetaID = qRegisterMetaType();
-Q_LOGGING_CATEGORY(scriptengineScript, "hifi.scriptengine.script")
-
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
QString message = "";
for (int i = 0; i < context->argumentCount(); i++) {
@@ -115,9 +113,9 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine *scriptEngine = qobject_cast(engine)) {
scriptEngine->print(message);
// prefix the script engine name to help disambiguate messages in the main debug log
- qCDebug(scriptengineScript, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
+ qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
} else {
- qCDebug(scriptengineScript, "%s", qUtf8Printable(message));
+ qCDebug(scriptengine_script, "%s", qUtf8Printable(message));
}
return QScriptValue();
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index 7a9af2278c..3001666b5d 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -526,6 +526,9 @@ public:
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
bool isUserLoaded() const { return _isUserLoaded; }
+ void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; }
+ bool isQuitWhenFinished() const { return _quitWhenFinished; }
+
// NOTE - this is used by the TypedArray implementation. we need to review this for thread safety
ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
@@ -768,6 +771,8 @@ protected:
std::atomic _isUserLoaded { false };
bool _isReloading { false };
+ std::atomic _quitWhenFinished;
+
ArrayBufferClass* _arrayBufferClass;
AssetScriptingInterface* _assetScriptingInterface;
diff --git a/libraries/script-engine/src/ScriptEngineLogging.cpp b/libraries/script-engine/src/ScriptEngineLogging.cpp
index 392bc05129..b51d7c3780 100644
--- a/libraries/script-engine/src/ScriptEngineLogging.cpp
+++ b/libraries/script-engine/src/ScriptEngineLogging.cpp
@@ -13,3 +13,4 @@
Q_LOGGING_CATEGORY(scriptengine, "hifi.scriptengine")
Q_LOGGING_CATEGORY(scriptengine_module, "hifi.scriptengine.module")
+Q_LOGGING_CATEGORY(scriptengine_script, "hifi.scriptengine.script")
diff --git a/libraries/script-engine/src/ScriptEngineLogging.h b/libraries/script-engine/src/ScriptEngineLogging.h
index 62e46632a6..3096dd4927 100644
--- a/libraries/script-engine/src/ScriptEngineLogging.h
+++ b/libraries/script-engine/src/ScriptEngineLogging.h
@@ -16,6 +16,7 @@
Q_DECLARE_LOGGING_CATEGORY(scriptengine)
Q_DECLARE_LOGGING_CATEGORY(scriptengine_module)
+Q_DECLARE_LOGGING_CATEGORY(scriptengine_script)
#endif // hifi_ScriptEngineLogging_h
diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp
index f2ed296b63..ad6e1debe9 100644
--- a/libraries/script-engine/src/ScriptEngines.cpp
+++ b/libraries/script-engine/src/ScriptEngines.cpp
@@ -347,7 +347,8 @@ void ScriptEngines::saveScripts() {
{
QReadLocker lock(&_scriptEnginesHashLock);
for (auto it = _scriptEnginesHash.begin(); it != _scriptEnginesHash.end(); ++it) {
- if (it.value() && it.value()->isUserLoaded()) {
+ // Save user-loaded scripts, only if they are set to quit when finished
+ if (it.value() && it.value()->isUserLoaded() && !it.value()->isQuitWhenFinished()) {
auto normalizedUrl = normalizeScriptURL(it.key());
list.append(normalizedUrl.toString());
}
@@ -456,7 +457,7 @@ void ScriptEngines::reloadAllScripts() {
}
ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor,
- bool activateMainWindow, bool reload) {
+ bool activateMainWindow, bool reload, bool quitWhenFinished) {
if (thread() != QThread::currentThread()) {
ScriptEnginePointer result { nullptr };
BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptEnginePointer, result),
@@ -488,6 +489,7 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
scriptEngine = ScriptEnginePointer(new ScriptEngine(_context, NO_SCRIPT, "about:" + scriptFilename.fileName()));
addScriptEngine(scriptEngine);
scriptEngine->setUserLoaded(isUserLoaded);
+ scriptEngine->setQuitWhenFinished(quitWhenFinished);
if (scriptFilename.isEmpty() || !scriptUrl.isValid()) {
launchScriptEngine(scriptEngine);
@@ -496,6 +498,11 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
connect(scriptEngine.data(), &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded);
connect(scriptEngine.data(), &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
+ // Shutdown Interface when script finishes, if requested
+ if (quitWhenFinished) {
+ connect(scriptEngine.data(), &ScriptEngine::finished, this, &ScriptEngines::quitWhenFinished);
+ }
+
// get the script engine object to load the script at the designated script URL
scriptEngine->loadURL(scriptUrl, reload);
}
@@ -536,6 +543,10 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
emit scriptCountChanged();
}
+void ScriptEngines::quitWhenFinished() {
+ qApp->quit();
+}
+
int ScriptEngines::runScriptInitializers(ScriptEnginePointer scriptEngine) {
int ii=0;
for (auto initializer : _scriptInitializers) {
diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h
index 376bae4827..4d5964e462 100644
--- a/libraries/script-engine/src/ScriptEngines.h
+++ b/libraries/script-engine/src/ScriptEngines.h
@@ -88,10 +88,11 @@ public:
* @param {boolean} [loadScriptFromEditor=false]
* @param {boolean} [activateMainWindow=false]
* @param {boolean} [reload=false]
+ * @param {boolean} [quitWhenFinished=false]
* @returns {boolean}
*/
Q_INVOKABLE ScriptEnginePointer loadScript(const QUrl& scriptFilename = QString(),
- bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
+ bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false, bool quitWhenFinished = false);
/**jsdoc
* @function ScriptDiscoveryService.stopScript
@@ -266,6 +267,7 @@ protected:
ScriptEnginePointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); }
void removeScriptEngine(ScriptEnginePointer);
void onScriptEngineLoaded(const QString& scriptFilename);
+ void quitWhenFinished();
void onScriptEngineError(const QString& scriptFilename);
void launchScriptEngine(ScriptEnginePointer);
diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h
index f214c3f11c..e80d38239f 100644
--- a/libraries/script-engine/src/UsersScriptingInterface.h
+++ b/libraries/script-engine/src/UsersScriptingInterface.h
@@ -106,10 +106,11 @@ public slots:
void mute(const QUuid& nodeID);
/**jsdoc
- * Get the user name and machine fingerprint associated with the given UUID. This will only do anything if you're an admin
- * of the domain you're in.
- * @function Users.getUsernameFromID
- * @param {Uuid} nodeID The node or session ID of the user whose username you want.
+ * Request the user name and machine fingerprint associated with the given UUID. The user name will be returned in a
+ * {@link Users.usernameFromIDReply|usernameFromIDReply} signal. This will only do anything if you're an admin of the domain
+ * you're in.
+ * @function Users.requestUsernameFromID
+ * @param {Uuid} nodeID The node or session ID of the user whose user name you want.
*/
void requestUsernameFromID(const QUuid& nodeID);
@@ -170,7 +171,8 @@ signals:
void enteredIgnoreRadius();
/**jsdoc
- * Notifies scripts of the user name and machine fingerprint associated with a UUID.
+ * Triggered in response to a {@link Users.requestUsernameFromID|requestUsernameFromID} call. Provides the user name and
+ * machine fingerprint associated with a UUID.
* Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin.
* @function Users.usernameFromIDReply
* @param {Uuid} nodeID
diff --git a/libraries/test-utils/CMakeLists.txt b/libraries/test-utils/CMakeLists.txt
new file mode 100644
index 0000000000..2c23e96c1e
--- /dev/null
+++ b/libraries/test-utils/CMakeLists.txt
@@ -0,0 +1,3 @@
+set(TARGET_NAME test-utils)
+setup_hifi_library(Network Gui)
+
diff --git a/libraries/test-utils/src/test-utils/FileDownloader.cpp b/libraries/test-utils/src/test-utils/FileDownloader.cpp
new file mode 100644
index 0000000000..09049e3e0c
--- /dev/null
+++ b/libraries/test-utils/src/test-utils/FileDownloader.cpp
@@ -0,0 +1,21 @@
+#include "FileDownloader.h"
+
+#include
+#include
+
+FileDownloader::FileDownloader(QUrl url, const Handler& handler, QObject* parent) : QObject(parent), _handler(handler) {
+ connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
+ _accessManager.get(QNetworkRequest(url));
+}
+
+void FileDownloader::waitForDownload() {
+ while (!_complete) {
+ QCoreApplication::processEvents();
+ }
+}
+
+void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
+ _handler(pReply->readAll());
+ pReply->deleteLater();
+ _complete = true;
+}
diff --git a/libraries/test-utils/src/test-utils/FileDownloader.h b/libraries/test-utils/src/test-utils/FileDownloader.h
new file mode 100644
index 0000000000..5a618fcf45
--- /dev/null
+++ b/libraries/test-utils/src/test-utils/FileDownloader.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include
+#include
+
+class FileDownloader : public QObject {
+ Q_OBJECT
+
+public:
+ using Handler = std::function;
+
+ FileDownloader(QUrl url, const Handler& handler, QObject* parent = 0);
+
+ void waitForDownload();
+
+private slots:
+ void fileDownloaded(QNetworkReply* pReply);
+
+private:
+ QNetworkAccessManager _accessManager;
+ Handler _handler;
+ bool _complete { false };
+};
diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
index da9ea46cf4..5df20ff30c 100644
--- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
+++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
@@ -54,6 +54,12 @@ public:
void setFullscreen(const QScreen* targetScreen, bool hideMenu = false);
void unsetFullscreen(const QScreen* avoidScreen = nullptr);
+ // FIXME remove access tot he menu from the plugin container
+ // Instead let display plugins expose a structure about the kinds
+ // of actions and menu items they want to have appear when they are
+ // active and allow the application to act on that when the display
+ // plugin becomes active (or when the UI is initialized, and a
+ // display plugin is already active)
virtual ui::Menu* getPrimaryMenu() = 0;
virtual void showDisplayPluginsTools(bool show = true) = 0;
virtual void requestReset() = 0;
diff --git a/libraries/ui/src/VirtualPadManager.h b/libraries/ui/src/VirtualPadManager.h
index f5afde9a64..6391d731f8 100644
--- a/libraries/ui/src/VirtualPadManager.h
+++ b/libraries/ui/src/VirtualPadManager.h
@@ -62,7 +62,7 @@ namespace VirtualPad {
private:
Instance _leftVPadInstance;
- bool _enabled;
+ bool _enabled {true};
bool _hidden;
glm::vec2 _jumpButtonPosition;
int _extraBottomMargin {0};
diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp
index d8f2db75c9..7e337070d3 100644
--- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp
+++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp
@@ -166,12 +166,11 @@ void OculusBaseDisplayPlugin::deactivateSession() {
//_session = nullptr;
}
void OculusBaseDisplayPlugin::updatePresentPose() {
- //mat4 sensorResetMat;
- //_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
- //_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, _currentFrame->frameIndex);
- //auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
- //_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose);
- _currentPresentFrameInfo.presentPose = _currentPresentFrameInfo.renderPose;
+ _currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
+ _currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0);
+ auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
+ _currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose);
+ _currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose;
}
OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() {
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
index 714cb91b3f..5a7417cb49 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
@@ -36,7 +36,6 @@
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
-const char* StandingHMDSensorMode { "Standing HMD Sensor Mode" }; // this probably shouldn't be hardcoded here
const char* OpenVrThreadedSubmit { "OpenVR Threaded Submit" }; // this probably shouldn't be hardcoded here
PoseData _nextRenderPoseData;
@@ -451,7 +450,6 @@ bool OpenVrDisplayPlugin::internalActivate() {
qDebug() << "OpenVR Threaded submit enabled: " << _threadedSubmit;
_openVrDisplayActive = true;
- _container->setIsOptionChecked(StandingHMDSensorMode, true);
_system->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y);
// Recommended render target size is per-eye, so double the X size for
// left + right eyes
@@ -507,7 +505,6 @@ void OpenVrDisplayPlugin::internalDeactivate() {
Parent::internalDeactivate();
_openVrDisplayActive = false;
- _container->setIsOptionChecked(StandingHMDSensorMode, false);
if (_system) {
// TODO: Invalidate poses. It's fine if someone else sets these shared values, but we're about to stop updating them, and
// we don't want ViveControllerManager to consider old values to be valid.
diff --git a/scripts/system/+android/displayNames.js b/scripts/system/+android/displayNames.js
new file mode 100644
index 0000000000..509d85cd2b
--- /dev/null
+++ b/scripts/system/+android/displayNames.js
@@ -0,0 +1,167 @@
+"use strict";
+//
+// displayNames.js
+// scripts/system/
+//
+// Created by Cristian Duarte & Gabriel Calero on May 3, 2018
+// Copyright 2018 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
+
+var MAX_DISTANCE_PX = 20; // Should we use dp instead?
+var UNKNOWN_NAME = "Unknown";
+var METERS_ABOVE_HEAD = 0.4;
+
+var TEXT_LINE_HEIGHT = .1;
+var TEXT_MARGIN = 0.025;
+
+var HIDE_MS = 10000;
+
+var currentTouchToAnalyze = null;
+
+var currentlyShownAvatar = {
+ avatarID: null,
+ avatar: null,
+ overlay: null
+};
+
+var logEnabled = false;
+
+var hideTimer = null;
+
+function printd(str) {
+ if (logEnabled) {
+ print("[displayNames.js] " + str);
+ }
+}
+
+function clearOverlay() {
+ currentlyShownAvatar.avatar = null;
+ if (currentlyShownAvatar.overlay) {
+ Overlays.editOverlay(currentlyShownAvatar.overlay, {visible: false});
+ }
+}
+
+function touchedAvatar(avatarID, avatarData) {
+ printd("[AVATARNAME] touchEnd FOUND " + JSON.stringify(avatarData));
+
+ if (hideTimer) {
+ Script.clearTimeout(hideTimer);
+ }
+
+ // Case: touching an already selected avatar
+ if (currentlyShownAvatar.avatar && currentlyShownAvatar.avatarID == avatarID) {
+ clearOverlay();
+ return;
+ }
+
+ // Save currently selected avatar
+ currentlyShownAvatar.avatarID = avatarID;
+ currentlyShownAvatar.avatar = avatarData;
+
+ if (currentlyShownAvatar.overlay == null) {
+ var over = Overlays.addOverlay("text3d", {
+ lineHeight: TEXT_LINE_HEIGHT,
+ color: { red: 255, green: 255, blue: 255},
+ backgroundColor: {red: 0, green: 0, blue: 0},
+ leftMargin: TEXT_MARGIN,
+ topMargin: TEXT_MARGIN,
+ rightMargin: TEXT_MARGIN,
+ bottomMargin: TEXT_MARGIN,
+ alpha: 0.6,
+ solid: true,
+ isFacingAvatar: true,
+ visible: false
+ });
+ currentlyShownAvatar.overlay = over;
+ }
+
+ var nameToShow = avatarData.displayName ? avatarData.displayName :
+ (avatarData.sessionDisplayName ? avatarData.sessionDisplayName : UNKNOWN_NAME);
+ var textSize = Overlays.textSize(currentlyShownAvatar.overlay, nameToShow);
+
+ Overlays.editOverlay(currentlyShownAvatar.overlay, {
+ dimensions: {
+ x: textSize.width + 2 * TEXT_MARGIN,
+ y: TEXT_LINE_HEIGHT + 2 * TEXT_MARGIN
+ },
+ localPosition: { x: 0, y: METERS_ABOVE_HEAD, z: 0 },
+ text: nameToShow,
+ parentID: avatarData.sessionUUID,
+ parentJointIndex: avatarData.getJointIndex("Head"),
+ visible: true
+ });
+
+ hideTimer = Script.setTimeout(function() {
+ clearOverlay();
+ }, HIDE_MS);
+}
+
+function touchBegin(event) {
+ currentTouchToAnalyze = event;
+}
+
+function touchEnd(event) {
+ if (Vec3.distance({x: event.x, y: event.y }, {x: currentTouchToAnalyze.x, y: currentTouchToAnalyze.y}) > MAX_DISTANCE_PX) {
+ printd("[AVATARNAME] touchEnd moved too much");
+ currentTouchToAnalyze = null;
+ return;
+ }
+
+ var pickRay = Camera.computePickRay(event.x, event.y);
+ var avatarRay = AvatarManager.findRayIntersection(pickRay, [], [MyAvatar.sessionUUID])
+
+ if (avatarRay.intersects) {
+ touchedAvatar(avatarRay.avatarID, AvatarManager.getAvatar(avatarRay.avatarID));
+ } else {
+ printd("[AVATARNAME] touchEnd released outside the avatar");
+ }
+
+ currentTouchToAnalyze = null;
+}
+
+var runAtLeastOnce = false;
+
+function ending() {
+ if (!runAtLeastOnce) {
+ return;
+ }
+
+ Controller.touchBeginEvent.disconnect(touchBegin);
+ Controller.touchEndEvent.disconnect(touchEnd);
+ Controller.mousePressEvent.disconnect(touchBegin);
+ Controller.mouseReleaseEvent.disconnect(touchEnd);
+
+ if (currentlyShownAvatar.overlay) {
+ Overlays.deleteOverlay(currentlyShownAvatar.overlay);
+ currentlyShownAvatar.overlay = null;
+ }
+ if (currentlyShownAvatar.avatar) {
+ currentlyShownAvatar.avatar = null;
+ }
+}
+
+function init() {
+ Controller.touchBeginEvent.connect(touchBegin);
+ Controller.touchEndEvent.connect(touchEnd);
+ Controller.mousePressEvent.connect(touchBegin);
+ Controller.mouseReleaseEvent.connect(touchEnd);
+
+ Script.scriptEnding.connect(function () {
+ ending();
+ });
+
+ runAtLeastOnce = true;
+}
+
+module.exports = {
+ init: init,
+ ending: ending
+}
+
+//init(); // Enable to use in desktop as a standalone
+
+}()); // END LOCAL_SCOPE
\ No newline at end of file
diff --git a/scripts/system/+android/modes.js b/scripts/system/+android/modes.js
index 941ecf7422..9e952e3cb6 100644
--- a/scripts/system/+android/modes.js
+++ b/scripts/system/+android/modes.js
@@ -28,6 +28,7 @@ modeLabel[MODE_MY_VIEW]="MY VIEW";
var logEnabled = false;
var radar = Script.require('./radar.js');
var uniqueColor = Script.require('./uniqueColor.js');
+var displayNames = Script.require('./displayNames.js');
function printd(str) {
if (logEnabled) {
@@ -95,8 +96,10 @@ function switchToMode(newMode) {
if (currentMode == MODE_RADAR) {
radar.startRadarMode();
+ displayNames.ending();
} else if (currentMode == MODE_MY_VIEW) {
// nothing to do yet
+ displayNames.init();
} else {
printd("Unknown view mode " + currentMode);
}
diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js
index 8d4c462f78..5d93ed4db1 100644
--- a/scripts/system/+android/radar.js
+++ b/scripts/system/+android/radar.js
@@ -21,7 +21,8 @@ function printd(str) {
}
var radar = false;
-var radarHeight = 10; // camera position meters above the avatar
+var RADAR_HEIGHT_INIT_DELTA = 10;
+var radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA; // camera position (absolute y)
var tablet;
var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar
@@ -45,12 +46,12 @@ var uniqueColor;
function moveTo(position) {
if (radar) {
- MyAvatar.position = position;
- Camera.position = Vec3.sum(MyAvatar.position, {
- x : 0,
+ MyAvatar.goToLocation(position, false);
+ Camera.position = {
+ x : position.x,
y : radarHeight,
- z : 0
- });
+ z : position.z
+ };
}
}
@@ -89,46 +90,6 @@ function keyPressEvent(event) {
}
}
-function actionOnObjectFromEvent(event) {
- var rayIntersection = findRayIntersection(Camera.computePickRay(event.x,
- event.y));
- if (rayIntersection && rayIntersection.intersects
- && rayIntersection.overlayID) {
- printd("found overlayID touched " + rayIntersection.overlayID);
- if (entitiesByOverlayID[rayIntersection.overlayID]) {
- var entity = Entities.getEntityProperties(
- entitiesByOverlayID[rayIntersection.overlayID],
- [ "sourceUrl" ]);
- Window.openUrl(entity.sourceUrl);
- return true;
- }
- }
- if (rayIntersection && rayIntersection.intersects
- && rayIntersection.entityID && rayIntersection.properties) {
- printd("found " + rayIntersection.entityID + " of type "
- + rayIntersection.properties.type);
- if (rayIntersection.properties.type == "Web") {
- printd("found web element to "
- + rayIntersection.properties.sourceUrl);
- Window.openUrl(rayIntersection.properties.sourceUrl);
- return true;
- }
- }
- return false;
-}
-
-function mousePress(event) {
- mousePressOrTouchEnd(event);
-}
-
-function mousePressOrTouchEnd(event) {
- if (radar) {
- if (actionOnObjectFromEvent(event)) {
- return;
- }
- }
-}
-
function toggleRadarMode() {
if (radar) {
endRadar();
@@ -229,9 +190,6 @@ function touchEnd(event) {
if (analyzeDoubleTap(event))
return; // double tap detected, finish
- if (radar) {
- mousePressOrTouchEnd(event);
- }
}
/**
@@ -386,12 +344,13 @@ function pinchUpdate(event) {
radarHeight -= pinchIncrement;
}
}
- var deltaHeight = avatarY + radarHeight - Camera.position.y;
- Camera.position = Vec3.sum(Camera.position, {
- x : 0,
- y : deltaHeight,
- z : 0
- });
+
+ Camera.position = {
+ x : Camera.position.x,
+ y : radarHeight,
+ z : Camera.position.z
+ };
+
if (!draggingCamera) {
startedDraggingCamera = true;
draggingCamera = true;
@@ -401,7 +360,8 @@ function pinchUpdate(event) {
}
function isInsideSquare(coords0, coords1, halfside) {
- return Math.abs(coords0.x - coords1.x) <= halfside
+ return coords0 != undefined && coords1 != undefined &&
+ Math.abs(coords0.x - coords1.x) <= halfside
&& Math.abs(coords0.y - coords1.y) <= halfside;
}
@@ -412,7 +372,7 @@ function dragScrollUpdate(event) {
// drag management
var pickRay = Camera.computePickRay(event.x, event.y);
var dragAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
- radarHeight));
+ radarHeight - MyAvatar.position.y));
if (lastDragAt === undefined || lastDragAt === null) {
lastDragAt = dragAt;
@@ -654,6 +614,7 @@ function Teleporter() {
return;
}
+
Camera.position = Vec3.sum(Camera.position, {
x : xDelta,
y : 0,
@@ -722,7 +683,7 @@ function Teleporter() {
return {
dragTeleportBegin : function(event) {
printd("[newTeleport] TELEPORT began");
- var overlayDimensions = entityIconModelDimensions();
+ var overlayDimensions = teleportIconModelDimensions(MyAvatar.position.y);
// var destination = computeDestination(event, MyAvatar.position,
// Camera.position, radarHeight);
// Dimension teleport and cancel overlays (not show them yet)
@@ -843,7 +804,7 @@ var avatarIconDimensionsVal = {
};
function avatarIconPlaneDimensions() {
// given the current height, give a size
- var xy = -0.003531 * radarHeight + 0.1;
+ var xy = -0.003531 * (radarHeight - MyAvatar.position.y) + 0.1;
avatarIconDimensionsVal.x = Math.abs(xy);
avatarIconDimensionsVal.y = Math.abs(xy);
// reuse object
@@ -1121,172 +1082,20 @@ function renderAllOthersAvatarIcons() {
}
}
-function entityAdded(entityID) {
- printd("Entity added " + entityID);
- var props = Entities.getEntityProperties(entityID, [ "type" ]);
- printd("Entity added " + entityID + " PROPS " + JSON.stringify(props));
- if (props && props.type == "Web") {
- printd("Entity Web added " + entityID);
- saveEntityData(entityID, true);
- }
-}
-
-function entityRemoved(entityID) {
- printd("Entity removed " + entityID);
- var props = Entities.getEntityProperties(entityID, [ "type" ]);
- if (props && props.type == "Web") {
- print("Entity Web removed " + entityID);
- removeEntityData(entityID);
- }
-}
-
-/*******************************************************************************
- * Entities (to remark) cache structure for showing entities markers
- ******************************************************************************/
-
-var entitiesData = {}; // by entityID
-var entitiesByOverlayID = {}; // by overlayID
-var entitiesIcons = []; // a parallel list of icons (overlays) to easily run
- // through
-
-var ICON_ENTITY_WEB_MODEL_URL = Script.resolvePath("../assets/images/web.svg");
-var ICON_ENTITY_IMG_MODEL_URL = Script
- .resolvePath("../assets/models/teleport-cancel.fbx"); // FIXME - use
- // correct
- // model&texture
var ICON_ENTITY_DEFAULT_DIMENSIONS = {
x : 0.10,
y : 0.00001,
z : 0.10
};
-var entityIconModelDimensionsVal = {
- x : 0,
- y : 0.00001,
- z : 0
-};
-function entityIconModelDimensions() {
- // given the current height, give a size
- var xz = -0.002831 * radarHeight + 0.1;
- entityIconModelDimensionsVal.x = xz;
- entityIconModelDimensionsVal.z = xz;
+
+function teleportIconModelDimensions(y) {
+ var teleportModelDimensions = ICON_ENTITY_DEFAULT_DIMENSIONS;
+ var xz = -0.002831 * (radarHeight - y) + 0.1;
+ teleportModelDimensions.x = xz;
+ teleportModelDimensions.z = xz;
// reuse object
- return entityIconModelDimensionsVal;
-}
-/*
- * entityIconPlaneDimensions: similar to entityIconModelDimensions but using xy
- * plane
- */
-function entityIconPlaneDimensions() {
- var dim = entityIconModelDimensions();
- var z = dim.z;
- dim.z = dim.y;
- dim.y = z;
- return dim;
-}
-
-function currentOverlayForEntity(QUuid) {
- if (entitiesData[QUuid] != undefined) {
- return entitiesData[QUuid].icon;
- } else {
- return null;
- }
-}
-
-function saveEntityData(QUuid, planar) {
- if (QUuid == null)
- return;
- var entity = Entities.getEntityProperties(QUuid, [ "position" ]);
- printd("entity added save entity " + QUuid);
- if (entitiesData[QUuid] != undefined) {
- entitiesData[QUuid].position = entity.position;
- } else {
- var entityIcon = Overlays.addOverlay("image3d", {
- subImage : {
- x : 0,
- y : 0,
- width : 150,
- height : 150
- },
- url : ICON_ENTITY_WEB_MODEL_URL,
- dimensions : ICON_ENTITY_DEFAULT_DIMENSIONS,
- visible : false,
- ignoreRayIntersection : false,
- orientation : Quat.fromPitchYawRollDegrees(-90, 0, 0)
- });
-
- entitiesIcons.push(entityIcon);
- entitiesData[QUuid] = {
- position : entity.position,
- icon : entityIcon
- };
- entitiesByOverlayID[entityIcon] = QUuid;
- }
-}
-
-function removeEntityData(QUuid) {
- if (QUuid == null)
- return;
-
- var itsOverlay = currentOverlayForEntity(QUuid);
- if (itsOverlay != null) {
- Overlays.deleteOverlay(itsOverlay);
- delete entitiesByOverlayID[itsOverlay];
- }
- var idx = entitiesIcons.indexOf(itsOverlay);
- entitiesIcons.splice(idx, 1);
-
- delete entitiesData[QUuid];
-}
-
-/*******************************************************************************
- * Entities to remark Icon/Markers rendering
- ******************************************************************************/
-
-function hideAllEntitiesIcons() {
- var len = entitiesIcons.length;
- for (var i = 0; i < len; i++) {
- Overlays.editOverlay(entitiesIcons[i], {
- visible : false
- });
- }
-}
-
-function renderAllEntitiesIcons() {
- var entityPos;
- var entityProps;
- var iconDimensions = entityIconModelDimensions();
- var planeDimensions = entityIconPlaneDimensions(); // plane overlays uses
- // xy instead of xz
- for ( var QUuid in entitiesData) {
- if (entitiesData.hasOwnProperty(QUuid)) {
- entityProps = Entities.getEntityProperties(QUuid, [ "position",
- "visible" ]);
- if (entityProps != null) {
- entityPos = entityProps.position;
- if (entitiesData[QUuid].icon != undefined && entityPos) {
- var iconPos = findLineToHeightIntersectionCoords(
- entityPos.x,
- entityPos.y
- + RADAR_ICONS_APPARENT_DISTANCE_TO_AVATAR_BASE,
- entityPos.z, Camera.position.x, Camera.position.y,
- Camera.position.z, Camera.position.y
- - RADAR_CAMERA_DISTANCE_TO_ICONS);
- if (!iconPos) {
- printd("entity icon pos bad for " + QUuid);
- continue;
- }
- var dimensions = entitiesData[QUuid].planar ? planeDimensions
- : iconDimensions;
- Overlays.editOverlay(entitiesData[QUuid].icon, {
- visible : entityProps.visible,
- dimensions : dimensions,
- position : iconPos
- });
- }
- }
- }
- }
+ return teleportModelDimensions;
}
/*******************************************************************************
@@ -1298,11 +1107,8 @@ function startRadar() {
saveAllOthersAvatarsData();
Camera.mode = "independent";
- Camera.position = Vec3.sum(MyAvatar.position, {
- x : 0,
- y : radarHeight,
- z : 0
- });
+ initCameraOverMyAvatar();
+
Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0);
radar = true;
@@ -1319,7 +1125,6 @@ function endRadar() {
Controller.setVPadEnabled(true);
disconnectRadarModeEvents();
- hideAllEntitiesIcons();
hideAllAvatarIcons();
}
@@ -1353,12 +1158,10 @@ function updateRadar() {
// Update avatar icons
if (startedDraggingCamera) {
hideAllAvatarIcons();
- hideAllEntitiesIcons();
startedDraggingCamera = false;
} else if (!draggingCamera) {
renderMyAvatarIcon();
renderAllOthersAvatarIcons();
- renderAllEntitiesIcons();
}
}
@@ -1366,48 +1169,41 @@ function valueIfDefined(value) {
return value !== undefined ? value : "";
}
-function entitiesAnalysis() {
- var ids = Entities.findEntitiesInFrustum(Camera.frustum);
- var entities = [];
- for (var i = 0; i < ids.length; i++) {
- var id = ids[i];
- var properties = Entities.getEntityProperties(id);
- entities.push({
- id : id,
- name : properties.name,
- type : properties.type,
- url : properties.type == "Model" ? properties.modelURL : "",
- sourceUrl : properties.sourceUrl,
- locked : properties.locked,
- visible : properties.visible,
- drawCalls : valueIfDefined(properties.renderInfo.drawCalls),
- hasScript : properties.script !== ""
- });
- }
-}
-
function connectRadarModeEvents() {
Script.update.connect(updateRadar); // 60Hz loop
Controller.keyPressEvent.connect(keyPressEvent);
- Controller.mousePressEvent.connect(mousePress); // single click/touch
Controller.touchUpdateEvent.connect(touchUpdate);
+ Window.domainChanged.connect(domainChanged);
MyAvatar.positionGoneTo.connect(positionGoneTo);
}
-function positionGoneTo() {
- Camera.position = Vec3.sum(MyAvatar.position, {
- x : 0,
+function initCameraOverMyAvatar() {
+ radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA;
+ Camera.position = {
+ x : MyAvatar.position.x,
y : radarHeight,
- z : 0
- });
+ z : MyAvatar.position.z
+ };
+}
+
+function domainChanged() {
+ initCameraOverMyAvatar();
+}
+
+function positionGoneTo() {
+ Camera.position = {
+ x : MyAvatar.position.x,
+ y : radarHeight,
+ z : MyAvatar.position.z
+ };
}
function disconnectRadarModeEvents() {
Script.update.disconnect(updateRadar);
Controller.keyPressEvent.disconnect(keyPressEvent);
- Controller.mousePressEvent.disconnect(mousePress);
Controller.touchUpdateEvent.disconnect(touchUpdate);
MyAvatar.positionGoneTo.disconnect(positionGoneTo);
+ Window.domainChanged.disconnect(domainChanged);
}
function init() {
@@ -1418,7 +1214,4 @@ function init() {
AvatarList.avatarAddedEvent.connect(avatarAdded);
AvatarList.avatarRemovedEvent.connect(avatarRemoved);
-
- Entities.addingEntity.connect(entityAdded);
- Entities.deletingEntity.connect(entityRemoved);
}
diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js
index 1fce772ec8..1f9a95c819 100644
--- a/scripts/system/controllers/controllerModules/equipEntity.js
+++ b/scripts/system/controllers/controllerModules/equipEntity.js
@@ -6,11 +6,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
- getControllerJointIndex, enableDispatcherModule, disableDispatcherModule,
+/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera,
+ getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, entityIsFarGrabbedByOther,
Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions,
Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable,
- cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE, Uuid, unhighlightTargetEntity
+ cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode
*/
Script.include("/~/system/libraries/Xform.js");
@@ -781,7 +781,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
}
}
};
-
+
var clearGrabActions = function(entityID) {
var actionIDs = Entities.getActionIDs(entityID);
var myGrabTag = "grab-" + MyAvatar.sessionUUID;
@@ -794,7 +794,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
}
}
};
-
+
var onMousePress = function(event) {
if (isInEditMode() || !event.isLeftButton) { // don't consider any left clicks on the entity while in edit
return;
@@ -808,7 +808,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
if (hasEquipData && entityProperties.parentID === EMPTY_PARENT_ID && !entityIsFarGrabbedByOther(entityID)) {
entityProperties.id = entityID;
var rightHandPosition = MyAvatar.getJointPosition("RightHand");
- var leftHandPosition = MyAvatar.getJointPosition("LeftHand");
+ var leftHandPosition = MyAvatar.getJointPosition("LeftHand");
var distanceToRightHand = Vec3.distance(entityProperties.position, rightHandPosition);
var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition);
var leftHandAvailable = leftEquipEntity.targetEntityID === null;
@@ -828,7 +828,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
};
var onKeyPress = function(event) {
- if (event.text === UNEQUIP_KEY) {
+ if (event.text.toLowerCase() === UNEQUIP_KEY) {
if (rightEquipEntity.targetEntityID) {
rightEquipEntity.endEquipEntity();
}
diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js
index cc884cdbc7..66cd197abd 100644
--- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js
@@ -564,6 +564,9 @@ Script.include("/~/system/libraries/Xform.js");
}
} else if (this.distanceRotating) {
this.distanceRotate(otherFarGrabModule);
+ } else if (this.highlightedEntity) {
+ Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity);
+ this.highlightedEntity = null;
}
}
return this.exitIfDisabled(controllerData);
diff --git a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js
index 3180e58e2c..ac57c8691f 100644
--- a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js
+++ b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js
@@ -63,6 +63,9 @@
this.highlightedEntity = targetEntityID;
}
}
+ } else if (this.highlightedEntity) {
+ dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity);
+ this.highlightedEntity = null;
}
}
diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js
index a4e439fe2f..274a4264cd 100644
--- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js
@@ -10,7 +10,7 @@
propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable,
Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues,
TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity,
- HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity
+ HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid
*/
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
diff --git a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js
index 59ce79cfd1..962ae89bb9 100644
--- a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js
+++ b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js
@@ -7,12 +7,8 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
- getControllerJointIndex, getGrabbableData, enableDispatcherModule, disableDispatcherModule,
- propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable,
- Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues,
- TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity,
- HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, AddressManager
+/* global Script, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule,
+ makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, BUMPER_ON_VALUE, AddressManager
*/
(function() {
diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js
index d454d20a02..cf3a9cf14b 100644
--- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js
@@ -11,8 +11,7 @@
TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS,
findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH,
HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME,
- TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Selection, DISPATCHER_HOVERING_LIST, Uuid,
- highlightTargetEntity, unhighlightTargetEntity
+ TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity
*/
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
@@ -43,11 +42,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
[],
100);
-
- // XXX does handJointIndex change if the avatar changes?
- this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
- this.controllerJointIndex = getControllerJointIndex(this.hand);
-
this.thisHandIsParent = function(props) {
if (!props) {
return false;
@@ -62,8 +56,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
return true;
}
- var controllerJointIndex = this.controllerJointIndex;
- if (props.parentJointIndex === controllerJointIndex) {
+ if (props.parentJointIndex === getControllerJointIndex(this.hand)) {
return true;
}
@@ -102,7 +95,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
// } else {
// handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
// }
- handJointIndex = this.controllerJointIndex;
+ handJointIndex = getControllerJointIndex(this.hand);
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(targetProps.id, "startNearGrab", args);
diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js
index 0f876816b3..368d5c483b 100644
--- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js
+++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js
@@ -9,7 +9,7 @@
/* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex,
enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION,
makeDispatcherModuleParameters, Overlays, makeRunningValues, Vec3, resizeTablet, getTabletWidthFromSettings,
- NEAR_GRAB_RADIUS
+ NEAR_GRAB_RADIUS, HMD, Uuid
*/
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
@@ -37,7 +37,6 @@ Script.include("/~/system/libraries/utils.js");
// XXX does handJointIndex change if the avatar changes?
this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
- this.controllerJointIndex = getControllerJointIndex(this.hand);
this.getOtherModule = function() {
return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay;
diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js
index 560da57b20..3bf99ca26a 100644
--- a/scripts/system/controllers/controllerModules/teleport.js
+++ b/scripts/system/controllers/controllerModules/teleport.js
@@ -10,7 +10,7 @@
/* jslint bitwise: true */
-/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex,
+/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Vec3,
HMD, Uuid, AvatarList, Picks, Pointers, PickType
*/
diff --git a/scripts/system/edit.js b/scripts/system/edit.js
index 6bb815a597..523582836f 100644
--- a/scripts/system/edit.js
+++ b/scripts/system/edit.js
@@ -1165,17 +1165,11 @@ function setupModelMenus() {
});
modelMenuAddedDelete = true;
}
- Menu.addMenuItem({
- menuName: "Edit",
- menuItemName: "Entity List...",
- afterItem: "Entities",
- grouping: "Advanced"
- });
Menu.addMenuItem({
menuName: "Edit",
menuItemName: "Parent Entity to Last",
- afterItem: "Entity List...",
+ afterItem: "Entities",
grouping: "Advanced"
});
@@ -1297,7 +1291,6 @@ function cleanupModelMenus() {
Menu.removeMenuItem("Edit", "Parent Entity to Last");
Menu.removeMenuItem("Edit", "Unparent Entity");
- Menu.removeMenuItem("Edit", "Entity List...");
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
@@ -1659,8 +1652,6 @@ function handeMenuEvent(menuItem) {
Window.promptTextChanged.connect(onPromptTextChanged);
Window.promptAsync("URL of SVO to import", "");
}
- } else if (menuItem === "Entity List...") {
- entityListTool.toggleVisible();
} else if (menuItem === "Select All Entities In Box") {
selectAllEtitiesInCurrentSelectionBox(false);
} else if (menuItem === "Select All Entities Touching Box") {
diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js
index b9fd7f725c..2d4a2d3e97 100644
--- a/scripts/system/hmd.js
+++ b/scripts/system/hmd.js
@@ -18,7 +18,7 @@
var headset; // The preferred headset. Default to the first one found in the following list.
var displayMenuName = "Display";
var desktopMenuItemName = "Desktop";
-['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) {
+['HTC Vive', 'Oculus Rift', 'WindowMS'].forEach(function (name) {
if (!headset && Menu.menuItemExists(displayMenuName, name)) {
headset = name;
}
diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html
index d608ab63e5..7906a3c97f 100644
--- a/scripts/system/html/entityList.html
+++ b/scripts/system/html/entityList.html
@@ -14,7 +14,6 @@
-
diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index 8647dca035..8d63261f4c 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -20,7 +20,6 @@
-
diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html
index c0bd87988d..cd646fed51 100644
--- a/scripts/system/html/gridControls.html
+++ b/scripts/system/html/gridControls.html
@@ -16,7 +16,6 @@
-
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js
index 625aa26b00..88b3ccbf7c 100644
--- a/scripts/system/html/js/entityList.js
+++ b/scripts/system/html/js/entityList.js
@@ -444,8 +444,6 @@ function loaded() {
augmentSpinButtons();
- setUpKeyboardControl();
-
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
document.addEventListener("contextmenu", function (event) {
event.preventDefault();
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js
index 4b6329db44..2194b539ef 100644
--- a/scripts/system/html/js/entityProperties.js
+++ b/scripts/system/html/js/entityProperties.js
@@ -7,7 +7,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global alert, augmentSpinButtons, clearTimeout, console, document, Element, EventBridge,
- HifiEntityUI, JSONEditor, openEventBridge, setUpKeyboardControl, setTimeout, window, _ $ */
+ HifiEntityUI, JSONEditor, openEventBridge, setTimeout, window, _ $ */
var PI = 3.14159265358979;
var DEGREES_TO_RADIANS = PI / 180.0;
@@ -2157,8 +2157,6 @@ function loaded() {
augmentSpinButtons();
- setUpKeyboardControl();
-
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
document.addEventListener("contextmenu", function(event) {
event.preventDefault();
diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js
index be4271788e..79a169400a 100644
--- a/scripts/system/html/js/gridControls.js
+++ b/scripts/system/html/js/gridControls.js
@@ -129,8 +129,6 @@ function loaded() {
augmentSpinButtons();
- setUpKeyboardControl();
-
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
});
document.addEventListener("keydown", function (keyDown) {
diff --git a/scripts/system/html/js/keyboardControl.js b/scripts/system/html/js/keyboardControl.js
deleted file mode 100644
index 7a8a314c62..0000000000
--- a/scripts/system/html/js/keyboardControl.js
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// keyboardControl.js
-//
-// Created by David Rowe on 28 Sep 2016.
-// Copyright 2016 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 setUpKeyboardControl() {
-
- var lowerTimer = null;
- var isRaised = false;
- var KEYBOARD_HEIGHT = 200;
-
- function raiseKeyboard() {
- window.isKeyboardRaised = true;
- window.isNumericKeyboard = this.type === "number";
-
- if (lowerTimer !== null) {
- clearTimeout(lowerTimer);
- lowerTimer = null;
- }
-
- EventBridge.emitWebEvent("_RAISE_KEYBOARD" + (this.type === "number" ? "_NUMERIC" : ""));
-
- if (!isRaised) {
- var delta = this.getBoundingClientRect().bottom + 10 - (document.body.clientHeight - KEYBOARD_HEIGHT);
- if (delta > 0) {
- setTimeout(function () {
- document.body.scrollTop += delta;
- }, 500); // Allow time for keyboard to be raised in QML.
- }
- }
-
- isRaised = true;
- }
-
- function doLowerKeyboard() {
- window.isKeyboardRaised = false;
- window.isNumericKeyboard = false;
-
- EventBridge.emitWebEvent("_LOWER_KEYBOARD");
- lowerTimer = null;
- isRaised = false;
- }
-
- function lowerKeyboard() {
- // Delay lowering keyboard a little in case immediately raise it again.
- if (lowerTimer === null) {
- lowerTimer = setTimeout(doLowerKeyboard, 20);
- }
- }
-
- function documentBlur() {
- // Action any pending Lower keyboard event immediately upon leaving document window so that they don't interfere with
- // other Entities Editor tab.
- if (lowerTimer !== null) {
- clearTimeout(lowerTimer);
- doLowerKeyboard();
- }
- }
-
- var inputs = document.querySelectorAll("input[type=text], input[type=password], input[type=number], textarea");
- for (var i = 0, length = inputs.length; i < length; i++) {
- inputs[i].addEventListener("focus", raiseKeyboard);
- inputs[i].addEventListener("blur", lowerKeyboard);
- }
-
- window.addEventListener("blur", documentBlur);
-}
-
diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js
index 71dc5e4273..04ae01bad6 100644
--- a/scripts/system/libraries/controllerDispatcherUtils.js
+++ b/scripts/system/libraries/controllerDispatcherUtils.js
@@ -7,7 +7,7 @@
/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform,
- Selection,
+ Selection, Uuid,
MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true,
HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true,
DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true,
@@ -34,11 +34,12 @@
getGrabbableData:true,
entityIsGrabbable:true,
entityIsDistanceGrabbable:true,
+ getControllerJointIndexCacheTime:true,
+ getControllerJointIndexCache:true,
getControllerJointIndex:true,
propsArePhysical:true,
controllerDispatcherPluginsNeedSort:true,
projectOntoXYPlane:true,
- getChildrenProps:true,
projectOntoEntityXYPlane:true,
projectOntoOverlayXYPlane:true,
makeLaserLockInfo:true,
@@ -53,6 +54,8 @@
TEAR_AWAY_COUNT:true,
TEAR_AWAY_CHECK_TIME:true,
distanceBetweenPointAndEntityBoundingBox:true,
+ entityIsEquipped:true,
+ entityIsFarGrabbedByOther:true,
highlightTargetEntity:true,
clearHighlightedEntities:true,
unhighlightTargetEntity:true
@@ -265,20 +268,32 @@ entityIsDistanceGrabbable = function(props) {
return true;
};
-getControllerJointIndex = function (hand) {
- if (HMD.isHandControllerAvailable()) {
- var controllerJointIndex = -1;
- if (Camera.mode === "first person") {
- controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
- "_CONTROLLER_RIGHTHAND" :
- "_CONTROLLER_LEFTHAND");
- } else if (Camera.mode === "third person") {
- controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
- "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
- "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
- }
+getControllerJointIndexCacheTime = [0, 0];
+getControllerJointIndexCache = [-1, -1];
- return controllerJointIndex;
+getControllerJointIndex = function (hand) {
+ var GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME = 3000; // msecs
+
+ var now = Date.now();
+ if (now - getControllerJointIndexCacheTime[hand] > GET_CONTROLLERJOINTINDEX_CACHE_REFRESH_TIME) {
+ if (HMD.isHandControllerAvailable()) {
+ var controllerJointIndex = -1;
+ if (Camera.mode === "first person") {
+ controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
+ "_CONTROLLER_RIGHTHAND" :
+ "_CONTROLLER_LEFTHAND");
+ } else if (Camera.mode === "third person") {
+ controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
+ "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
+ "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
+ }
+
+ getControllerJointIndexCacheTime[hand] = now;
+ getControllerJointIndexCache[hand] = controllerJointIndex;
+ return controllerJointIndex;
+ }
+ } else {
+ return getControllerJointIndexCache[hand];
}
return -1;
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index a05778e2dd..dc4d5aa844 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -19,6 +19,7 @@ var selectionDisplay = null; // for gridTool.js to ignore
Script.include("/~/system/libraries/WebTablet.js");
Script.include("/~/system/libraries/gridTool.js");
+ Script.include("/~/system/libraries/connectionUtils.js");
var METAVERSE_SERVER_URL = Account.metaverseServerURL;
var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace";
@@ -789,10 +790,14 @@ var selectionDisplay = null; // for gridTool.js to ignore
var savedDisablePreviewOptionLocked = false;
var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");;
function maybeEnableHMDPreview() {
- setTabletVisibleInSecondaryCamera(true);
- DesktopPreviewProvider.setPreviewDisabledReason("USER");
- Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption);
- savedDisablePreviewOptionLocked = false;
+ // Set a small timeout to prevent sensitive data from being shown during
+ // UI fade
+ Script.setTimeout(function () {
+ setTabletVisibleInSecondaryCamera(true);
+ DesktopPreviewProvider.setPreviewDisabledReason("USER");
+ Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption);
+ savedDisablePreviewOptionLocked = false;
+ }, 150);
}
// Function Name: fromQml()
diff --git a/scripts/system/pal.js b/scripts/system/pal.js
index 0a01007ee9..c70c2729f5 100644
--- a/scripts/system/pal.js
+++ b/scripts/system/pal.js
@@ -251,6 +251,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
});
}
break;
+ case 'refresh': // old name for refreshNearby
case 'refreshNearby':
data = {};
ExtendedOverlay.some(function (overlay) { // capture the audio data
@@ -743,10 +744,13 @@ function receiveMessage(channel, messageString, senderID) {
var message = JSON.parse(messageString);
switch (message.method) {
case 'select':
- sendToQml(message); // Accepts objects, not just strings.
+ if (!onPalScreen) {
+ tablet.loadQMLSource(PAL_QML_SOURCE);
+ Script.setTimeout(function () { sendToQml(message); }, 1000);
+ } else {
+ sendToQml(message); // Accepts objects, not just strings.
+ }
break;
- default:
- print('Unrecognized PAL message', messageString);
}
}
diff --git a/tests-manual/CMakeLists.txt b/tests-manual/CMakeLists.txt
new file mode 100644
index 0000000000..bc64183298
--- /dev/null
+++ b/tests-manual/CMakeLists.txt
@@ -0,0 +1,8 @@
+# add the manual test directories
+file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*")
+foreach(DIR ${TEST_SUBDIRS})
+ if((IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}/CMakeLists.txt"))
+ set(TEST_PROJ_NAME ${DIR})
+ add_subdirectory(${DIR})
+ endif()
+endforeach()
diff --git a/tests/controllers/CMakeLists.txt b/tests-manual/controllers/CMakeLists.txt
similarity index 100%
rename from tests/controllers/CMakeLists.txt
rename to tests-manual/controllers/CMakeLists.txt
diff --git a/tests/controllers/qml/main.qml b/tests-manual/controllers/qml/main.qml
similarity index 100%
rename from tests/controllers/qml/main.qml
rename to tests-manual/controllers/qml/main.qml
diff --git a/tests/controllers/src/main.cpp b/tests-manual/controllers/src/main.cpp
similarity index 100%
rename from tests/controllers/src/main.cpp
rename to tests-manual/controllers/src/main.cpp
diff --git a/tests/entities/CMakeLists.txt b/tests-manual/entities/CMakeLists.txt
similarity index 100%
rename from tests/entities/CMakeLists.txt
rename to tests-manual/entities/CMakeLists.txt
diff --git a/tests/entities/packet.bin b/tests-manual/entities/packet.bin
similarity index 100%
rename from tests/entities/packet.bin
rename to tests-manual/entities/packet.bin
diff --git a/tests/entities/src/main.cpp b/tests-manual/entities/src/main.cpp
similarity index 100%
rename from tests/entities/src/main.cpp
rename to tests-manual/entities/src/main.cpp
diff --git a/tests/gl/CMakeLists.txt b/tests-manual/gl/CMakeLists.txt
similarity index 100%
rename from tests/gl/CMakeLists.txt
rename to tests-manual/gl/CMakeLists.txt
diff --git a/tests/gl/src/main.cpp b/tests-manual/gl/src/main.cpp
similarity index 100%
rename from tests/gl/src/main.cpp
rename to tests-manual/gl/src/main.cpp
diff --git a/tests-manual/gpu-textures/CMakeLists.txt b/tests-manual/gpu-textures/CMakeLists.txt
new file mode 100644
index 0000000000..c10f2eda3f
--- /dev/null
+++ b/tests-manual/gpu-textures/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(TARGET_NAME gpu-textures-tests)
+AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils)
+# This is not a testcase -- just set it up as a regular hifi project
+setup_hifi_project(Quick Gui Script)
+setup_memory_debugger()
+set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
+link_hifi_libraries(
+ shared task networking gl
+ ktx gpu octree
+ ${PLATFORM_GL_BACKEND}
+)
+
+set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/qml\"")
+package_libraries_for_deployment()
+
+target_nsight()
diff --git a/tests-manual/gpu-textures/qml/textureStats.qml b/tests-manual/gpu-textures/qml/textureStats.qml
new file mode 100644
index 0000000000..d3700c3eac
--- /dev/null
+++ b/tests-manual/gpu-textures/qml/textureStats.qml
@@ -0,0 +1,52 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.3
+
+Item {
+ width: 400
+ height: 600
+
+ Column {
+ spacing: 10
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.margins: 10
+
+ Text { text: qsTr("Total") }
+ Text { text: Stats.total + " MB" }
+ Text { text: qsTr("Allocated") }
+ Text { text: Stats.allocated }
+ Text { text: qsTr("Populated") }
+ Text { text: Stats.populated }
+ Text { text: qsTr("Pending") }
+ Text { text: Stats.pending }
+ Text { text: qsTr("Current Index") }
+ Text { text: Stats.index }
+ Text { text: qsTr("Current Source") }
+ Text { text: Stats.source }
+ Text { text: qsTr("Current Rez") }
+ Text { text: Stats.rez.width + " x " + Stats.rez.height }
+ }
+
+ Row {
+ id: row1
+ spacing: 10
+ anchors.bottom: row2.top
+ anchors.left: parent.left
+ anchors.margins: 10
+ Button { text: "1024"; onClicked: Stats.maxTextureMemory(1024); }
+ Button { text: "256"; onClicked: Stats.maxTextureMemory(256); }
+ }
+
+ Row {
+ id: row2
+ spacing: 10
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.margins: 10
+ Button { text: "Change Textures"; onClicked: Stats.changeTextures(); }
+ Button { text: "Next"; onClicked: Stats.nextTexture(); }
+ Button { text: "Previous"; onClicked: Stats.prevTexture(); }
+ }
+
+}
+
diff --git a/tests-manual/gpu-textures/src/TestHelpers.cpp b/tests-manual/gpu-textures/src/TestHelpers.cpp
new file mode 100644
index 0000000000..f952a4385f
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestHelpers.cpp
@@ -0,0 +1,26 @@
+//
+// Created by Bradley Austin Davis on 2016/05/16
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "TestHelpers.h"
+#include
+
+gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
+ auto vs = gpu::Shader::createVertex(vertexShaderSrc);
+ auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
+ auto shader = gpu::Shader::createProgram(vs, fs);
+ if (!gpu::Shader::makeProgram(*shader, bindings)) {
+ printf("Could not compile shader\n");
+ exit(-1);
+ }
+ return shader;
+}
+
+QString projectRootDir() {
+ static QString projectRootPath = QFileInfo(QFileInfo(__FILE__).absolutePath() + "/..").absoluteFilePath();
+ return projectRootPath;
+}
diff --git a/tests-manual/gpu-textures/src/TestHelpers.h b/tests-manual/gpu-textures/src/TestHelpers.h
new file mode 100644
index 0000000000..17730c3642
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestHelpers.h
@@ -0,0 +1,40 @@
+//
+// Created by Bradley Austin Davis on 2016/05/16
+// Copyright 2014 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
+//
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+struct RenderArgs {
+ gpu::ContextPointer _context;
+ ivec4 _viewport;
+ gpu::Batch* _batch;
+};
+
+class GpuTestBase : public QObject {
+public:
+ virtual ~GpuTestBase() {}
+ virtual bool isReady() const { return true; }
+ virtual size_t getTestCount() const { return 1; }
+ virtual void renderTest(size_t test, const RenderArgs& args) = 0;
+ virtual QObject * statsObject() { return nullptr; }
+ virtual QUrl statUrl() { return QUrl(); }
+};
+
+uint32_t toCompactColor(const glm::vec4& color);
+gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings);
+QString projectRootDir();
diff --git a/tests-manual/gpu-textures/src/TestTextures.cpp b/tests-manual/gpu-textures/src/TestTextures.cpp
new file mode 100644
index 0000000000..7aedb506da
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestTextures.cpp
@@ -0,0 +1,166 @@
+//
+// Created by Bradley Austin Davis on 2016/05/16
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "TestTextures.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "TestHelpers.h"
+
+std::string vertexShaderSource = R"SHADER(
+#line 14
+layout(location = 0) out vec2 outTexCoord0;
+
+const vec4 VERTICES[] = vec4[](
+ vec4(-1.0, -1.0, 0.0, 1.0),
+ vec4( 1.0, -1.0, 0.0, 1.0),
+ vec4(-1.0, 1.0, 0.0, 1.0),
+ vec4( 1.0, 1.0, 0.0, 1.0)
+);
+
+void main() {
+ outTexCoord0 = VERTICES[gl_VertexID].xy;
+ outTexCoord0 += 1.0;
+ outTexCoord0 /= 2.0;
+ gl_Position = VERTICES[gl_VertexID];
+}
+)SHADER";
+
+std::string fragmentShaderSource = R"SHADER(
+#line 28
+
+uniform sampler2D tex;
+
+layout(location = 0) in vec2 inTexCoord0;
+layout(location = 0) out vec4 outFragColor;
+
+void main() {
+ outFragColor = texture(tex, inTexCoord0);
+ outFragColor.a = 1.0;
+ //outFragColor.rb = inTexCoord0;
+}
+
+)SHADER";
+
+#define STAT_UPDATE(name, src) \
+ { \
+ auto val = src; \
+ if (_##name != val) { \
+ _##name = val; \
+ emit name##Changed(); \
+ } \
+ }
+
+
+void TextureTestStats::update(int curIndex, const gpu::TexturePointer& texture) {
+ STAT_UPDATE(total, (int)BYTES_TO_MB(gpu::Context::getTextureGPUMemSize()));
+ STAT_UPDATE(allocated, (int)gpu::Context::getTextureResourceGPUMemSize());
+ STAT_UPDATE(pending, (int)gpu::Context::getTexturePendingGPUTransferMemSize());
+ STAT_UPDATE(populated, (int)gpu::Context::getTextureResourcePopulatedGPUMemSize());
+ STAT_UPDATE(source, texture->source().c_str());
+ STAT_UPDATE(index, curIndex);
+ auto dims = texture->getDimensions();
+ STAT_UPDATE(rez, QSize(dims.x, dims.y));
+}
+
+TexturesTest::TexturesTest() {
+ connect(&stats, &TextureTestStats::changeTextures, this, &TexturesTest::onChangeTextures);
+ connect(&stats, &TextureTestStats::nextTexture, this, &TexturesTest::onNextTexture);
+ connect(&stats, &TextureTestStats::prevTexture, this, &TexturesTest::onPrevTexture);
+ connect(&stats, &TextureTestStats::maxTextureMemory, this, &TexturesTest::onMaxTextureMemory);
+ {
+ auto VS = gpu::Shader::createVertex(vertexShaderSource);
+ auto PS = gpu::Shader::createPixel(fragmentShaderSource);
+ auto program = gpu::Shader::createProgram(VS, PS);
+ gpu::Shader::BindingSet slotBindings;
+ gpu::Shader::makeProgram(*program, slotBindings);
+ // If the pipeline did not exist, make it
+ auto state = std::make_shared();
+ state->setCullMode(gpu::State::CULL_NONE);
+ state->setDepthTest({});
+ state->setBlendFunction({ false });
+ pipeline = gpu::Pipeline::create(program, state);
+ }
+
+ onChangeTextures();
+}
+
+
+void TexturesTest::renderTest(size_t testId, const RenderArgs& args) {
+ stats.update((int)index, textures[index]);
+ gpu::Batch& batch = *(args._batch);
+ batch.setPipeline(pipeline);
+ batch.setInputFormat(vertexFormat);
+ for (const auto& texture : textures) {
+ batch.setResourceTexture(0, texture);
+ batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
+ }
+ batch.setResourceTexture(0, textures[index]);
+ batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
+}
+
+#define LOAD_TEXTURE_COUNT 64
+
+void TexturesTest::onChangeTextures() {
+ static const QDir TEST_DIR("D:/ktx_texture_test");
+ static std::vector ALL_TEXTURE_FILES;
+ if (ALL_TEXTURE_FILES.empty()) {
+ auto entryList = TEST_DIR.entryList({ "*.ktx" }, QDir::Filter::Files);
+ ALL_TEXTURE_FILES.reserve(entryList.size());
+ for (auto entry : entryList) {
+ auto textureFile = TEST_DIR.absoluteFilePath(entry).toStdString();
+ ALL_TEXTURE_FILES.push_back(textureFile);
+ }
+ }
+
+ oldTextures.clear();
+ oldTextures.swap(textures);
+
+#if 0
+ static const std::string bad = "D:/ktx_texture_test/b4beed38675dbc7a827ecd576399c1f4.ktx";
+ auto texture = gpu::Texture::unserialize(bad);
+ auto texelFormat = texture->getTexelFormat();
+ qDebug() << texture->getTexelFormat().getSemantic();
+ qDebug() << texture->getTexelFormat().getScalarCount();
+ textures.push_back(texture);
+#else
+ std::shuffle(ALL_TEXTURE_FILES.begin(), ALL_TEXTURE_FILES.end(), std::default_random_engine());
+ size_t newTextureCount = std::min(ALL_TEXTURE_FILES.size(), LOAD_TEXTURE_COUNT);
+ for (size_t i = 0; i < newTextureCount; ++i) {
+ const auto& textureFile = ALL_TEXTURE_FILES[i];
+ auto texture = gpu::Texture::unserialize(textureFile);
+ qDebug() << textureFile.c_str();
+ qDebug() << texture->getTexelFormat().getSemantic();
+ qDebug() << texture->getTexelFormat().getScalarCount();
+ textures.push_back(texture);
+ }
+#endif
+ index = 0;
+ qDebug() << "Done";
+}
+
+void TexturesTest::onNextTexture() {
+ index += textures.size() + 1;
+ index %= textures.size();
+}
+
+void TexturesTest::onPrevTexture() {
+ index += textures.size() - 1;
+ index %= textures.size();
+}
+
+void TexturesTest::onMaxTextureMemory(int maxTextureMemory) {
+ gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(maxTextureMemory));
+}
diff --git a/tests-manual/gpu-textures/src/TestTextures.h b/tests-manual/gpu-textures/src/TestTextures.h
new file mode 100644
index 0000000000..e446ce6132
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestTextures.h
@@ -0,0 +1,74 @@
+//
+// Created by Bradley Austin Davis on 2016/05/16
+// Copyright 2014 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
+//
+#pragma once
+
+#include "TestHelpers.h"
+
+#define STATS_PROPERTY(type, name, initialValue) \
+ Q_PROPERTY(type name READ name NOTIFY name##Changed) \
+public: \
+ type name() { return _##name; }; \
+private: \
+ type _##name{ initialValue };
+
+
+class TextureTestStats : public QObject {
+ Q_OBJECT;
+ STATS_PROPERTY(int, pending, 0)
+ STATS_PROPERTY(int, total, 0)
+ STATS_PROPERTY(int, populated, 0)
+ STATS_PROPERTY(int, allocated, 0)
+ STATS_PROPERTY(int, index, 0)
+
+ STATS_PROPERTY(QString, source, QString())
+ STATS_PROPERTY(QSize, rez, QSize(0, 0))
+
+public:
+ void update(int index, const gpu::TexturePointer& texture);
+
+signals:
+ void pendingChanged();
+ void totalChanged();
+ void populatedChanged();
+ void allocatedChanged();
+ void changeTextures();
+ void rezChanged();
+ void indexChanged();
+ void sourceChanged();
+ void maxTextureMemory(int);
+
+ void nextTexture();
+ void prevTexture();
+};
+
+
+class TexturesTest : public GpuTestBase {
+ Q_OBJECT
+
+ gpu::Stream::FormatPointer vertexFormat { std::make_shared() };
+ std::vector textures;
+ std::vector oldTextures;
+ gpu::PipelinePointer pipeline;
+ TextureTestStats stats;
+ size_t index{ 0 };
+
+public:
+ TexturesTest();
+ QObject* statsObject() override { return &stats; }
+ QUrl statUrl() override { return QUrl::fromLocalFile(projectRootDir() + "/qml/textureStats.qml"); }
+ void renderTest(size_t testId, const RenderArgs& args) override;
+
+protected slots:
+ void onChangeTextures();
+ void onMaxTextureMemory(int newValue);
+ void onNextTexture();
+ void onPrevTexture();
+
+};
+
+
diff --git a/tests-manual/gpu-textures/src/TestWindow.cpp b/tests-manual/gpu-textures/src/TestWindow.cpp
new file mode 100644
index 0000000000..038b0b9b80
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestWindow.cpp
@@ -0,0 +1,117 @@
+//
+// Created by Bradley Austin Davis on 2016/05/16
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "TestWindow.h"
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+
+TestWindow::TestWindow() {
+
+ auto timer = new QTimer(this);
+ timer->setTimerType(Qt::PreciseTimer);
+ timer->setInterval(5);
+ connect(timer, &QTimer::timeout, [&] { draw(); });
+ timer->start();
+
+ connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] {
+ timer->stop();
+ _aboutToQuit = true;
+ });
+
+ setSurfaceType(QSurface::OpenGLSurface);
+
+ QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
+ format.setOption(QSurfaceFormat::DebugContext);
+ setFormat(format);
+ _glContext.setFormat(format);
+ _glContext.create();
+ _glContext.makeCurrent(this);
+
+ show();
+}
+
+void TestWindow::initGl() {
+ _glContext.makeCurrent(this);
+ gl::initModuleGl();
+ gpu::Context::init();
+ _renderArgs->_context = std::make_shared();
+ _glContext.makeCurrent(this);
+ resize(QSize(800, 600));
+}
+
+void TestWindow::resizeWindow(const QSize& size) {
+ _size = size;
+ _renderArgs->_viewport = ivec4(0, 0, _size.width(), _size.height());
+}
+
+void TestWindow::beginFrame() {
+ _renderArgs->_context->recycle();
+ _renderArgs->_context->beginFrame();
+ gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
+ batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
+ batch.clearDepthFramebuffer(1e4);
+ batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
+ });
+
+ gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
+ batch.setViewportTransform(_renderArgs->_viewport);
+ batch.setStateScissorRect(_renderArgs->_viewport);
+ batch.setProjectionTransform(_projectionMatrix);
+ });
+}
+
+void TestWindow::endFrame() {
+ gpu::doInBatch("TestWindow::endFrame::finish", _renderArgs->_context, [&](gpu::Batch& batch) {
+ batch.resetStages();
+ });
+ auto framePointer = _renderArgs->_context->endFrame();
+ _renderArgs->_context->consumeFrameUpdates(framePointer);
+ _renderArgs->_context->executeFrame(framePointer);
+ _glContext.swapBuffers(this);
+}
+
+void TestWindow::draw() {
+ if (_aboutToQuit) {
+ return;
+ }
+
+ // Attempting to draw before we're visible and have a valid size will
+ // produce GL errors.
+ if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
+ return;
+ }
+
+ if (!_glContext.makeCurrent(this)) {
+ return;
+ }
+
+ static std::once_flag once;
+ std::call_once(once, [&] { initGl(); });
+ beginFrame();
+
+ renderFrame();
+
+ endFrame();
+}
+
+void TestWindow::resizeEvent(QResizeEvent* ev) {
+ resizeWindow(ev->size());
+ float fov_degrees = 60.0f;
+ float aspect_ratio = (float)_size.width() / _size.height();
+ float near_clip = 0.1f;
+ float far_clip = 1000.0f;
+ _projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
+}
diff --git a/tests-manual/gpu-textures/src/TestWindow.h b/tests-manual/gpu-textures/src/TestWindow.h
new file mode 100644
index 0000000000..00bbf03836
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestWindow.h
@@ -0,0 +1,41 @@
+//
+// Created by Bradley Austin Davis on 2016/05/16
+// Copyright 2014 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
+//
+
+#pragma once
+
+#include
+#include
+
+#include
+#include
+#include
+#include "TestHelpers.h"
+
+#define DEFERRED_LIGHTING
+
+class TestWindow : public QWindow {
+protected:
+ QOpenGLContextWrapper _glContext;
+ QSize _size;
+ glm::mat4 _projectionMatrix;
+ bool _aboutToQuit { false };
+ std::shared_ptr _renderArgs{ std::make_shared() };
+
+ TestWindow();
+ virtual void initGl();
+ virtual void renderFrame() = 0;
+
+private:
+ void resizeWindow(const QSize& size);
+
+ void beginFrame();
+ void endFrame();
+ void draw();
+ void resizeEvent(QResizeEvent* ev) override;
+};
+
diff --git a/tests-manual/gpu-textures/src/main.cpp b/tests-manual/gpu-textures/src/main.cpp
new file mode 100644
index 0000000000..3d0051dc1d
--- /dev/null
+++ b/tests-manual/gpu-textures/src/main.cpp
@@ -0,0 +1,170 @@
+//
+// main.cpp
+// tests/gpu-test/src
+//
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include