Merge branch 'master' of https://github.com/highfidelity/hifi into black

This commit is contained in:
samcake 2017-08-24 11:05:28 -07:00
commit b6a4ec910c
44 changed files with 312 additions and 127 deletions

View file

@ -54,6 +54,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
if (message->getSize() == 0) {
return;
}
QDataStream packetStream(message->getMessage());
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it

View file

@ -458,7 +458,7 @@ const QString DISABLED_AUTOMATIC_NETWORKING_VALUE = "disabled";
bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
bool DomainServer::isPacketVerified(const udt::Packet& packet) {
PacketType headerType = NLPacket::typeInHeader(packet);
PacketVersion headerVersion = NLPacket::versionInHeader(packet);
@ -471,7 +471,48 @@ bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
DomainGatekeeper::sendProtocolMismatchConnectionDenial(packet.getSenderSockAddr());
}
// let the normal nodeList implementation handle all other packets.
if (!PacketTypeEnum::getNonSourcedPackets().contains(headerType)) {
// this is a sourced packet - first check if we have a node that matches
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
SharedNodePointer sourceNode = nodeList->nodeWithUUID(sourceID);
if (sourceNode) {
// unverified DS packets (due to a lack of connection secret between DS + node)
// must come either from the same public IP address or a local IP address (set by RFC 1918)
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sourceNode->getLinkedData());
bool exactAddressMatch = nodeData->getSendingSockAddr() == packet.getSenderSockAddr();
bool bothPrivateAddresses = nodeData->getSendingSockAddr().hasPrivateAddress()
&& packet.getSenderSockAddr().hasPrivateAddress();
if (nodeData && (exactAddressMatch || bothPrivateAddresses)) {
// to the best of our ability we've verified that this packet comes from the right place
// let the NodeList do its checks now (but pass it the sourceNode so it doesn't need to look it up again)
return nodeList->isPacketVerifiedWithSource(packet, sourceNode.data());
} else {
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unmatched IP for UUID";
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
qDebug() << "Packet of type" << headerType
<< "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID);
return false;
}
} else {
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID";
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
qDebug() << "Packet of type" << headerType
<< "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID);
return false;
}
}
// fallback to allow the normal NodeList implementation to verify packets
return nodeList->isPacketVerified(packet);
}
@ -570,7 +611,7 @@ void DomainServer::setupNodeListAndAssignments() {
addStaticAssignmentsToQueue();
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
nodeList->setPacketFilterOperator(&DomainServer::packetVersionMatch);
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
}
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
@ -853,7 +894,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
}
void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
QDataStream packetStream(message->getMessage());
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr(), false);

View file

@ -71,6 +71,7 @@ public slots:
void restart();
private slots:
void processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> packet);
void processListRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode);
@ -79,7 +80,6 @@ public slots:
void processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message);
void processICEServerHeartbeatACK(QSharedPointer<ReceivedMessage> message);
private slots:
void setupPendingAssignmentCredits();
void sendPendingTransactionsToServer();
@ -129,7 +129,7 @@ private:
void getTemporaryName(bool force = false);
static bool packetVersionMatch(const udt::Packet& packet);
static bool isPacketVerified(const udt::Packet& packet);
bool resetAccountManagerAccessToken();

View file

@ -18,6 +18,7 @@ find_package(Qt5LinguistTools REQUIRED)
find_package(Qt5LinguistToolsMacros)
if (WIN32)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -bigobj")
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
endif()

View file

@ -35,7 +35,6 @@ Column {
property string metaverseServerUrl: '';
property string actions: 'snapshot';
// sendToScript doesn't get wired until after everything gets created. So we have to queue fillDestinations on nextTick.
Component.onCompleted: delay.start();
property string labelText: actions;
property string filter: '';
onFilterChanged: filterChoicesByText();
@ -125,11 +124,9 @@ Column {
cb();
});
}
property var delay: Timer { // No setTimeout or nextTick in QML.
interval: 0;
onTriggered: fillDestinations();
}
function fillDestinations() { // Public
console.debug('Feed::fillDestinations()')
function report(label, error) {
console.log(label, actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
}

View file

@ -39,11 +39,24 @@ StackView {
property var rpcCounter: 0;
signal sendToScript(var message);
function rpc(method, parameters, callback) {
console.debug('TabletAddressDialog: rpc: method = ', method, 'parameters = ', parameters, 'callback = ', callback)
rpcCalls[rpcCounter] = callback;
var message = {method: method, params: parameters, id: rpcCounter++, jsonrpc: "2.0"};
sendToScript(message);
}
function fromScript(message) {
if (message.method === 'refreshFeeds') {
var feeds = [happeningNow, places, snapshots];
console.debug('TabletAddressDialog::fromScript: refreshFeeds', 'feeds = ', feeds);
feeds.forEach(function(feed) {
Qt.callLater(feed.fillDestinations);
});
return;
}
var callback = rpcCalls[message.id];
if (!callback) {
console.log('No callback for message fromScript', JSON.stringify(message));

View file

@ -1735,6 +1735,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
// Setup the mouse ray pick and related operators
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(_rayPickManager.createRayPick(
RayPickFilter(DependencyManager::get<RayPickScriptingInterface>()->PICK_ENTITIES() | DependencyManager::get<RayPickScriptingInterface>()->PICK_INCLUDE_NONCOLLIDABLE()),
0.0f, true));
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](QUuid rayPickID) {
RayToEntityIntersectionResult entityResult;
RayPickResult result = _rayPickManager.getPrevRayPickResult(rayPickID);
entityResult.intersects = result.type != DependencyManager::get<RayPickScriptingInterface>()->INTERSECTED_NONE();
if (entityResult.intersects) {
entityResult.intersection = result.intersection;
entityResult.distance = result.distance;
entityResult.surfaceNormal = result.surfaceNormal;
entityResult.entityID = result.objectID;
entityResult.entity = DependencyManager::get<EntityTreeRenderer>()->getTree()->findEntityByID(entityResult.entityID);
}
return entityResult;
});
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](QUuid rayPickID, bool value) {
_rayPickManager.setPrecisionPicking(rayPickID, value);
});
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
}

View file

@ -64,6 +64,7 @@ public:
// You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays.
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
void setPrecisionPicking(const bool precisionPicking) { DependencyManager::get<RayPickScriptingInterface>()->setPrecisionPicking(_rayPickUID, precisionPicking); }
void setIgnoreEntities(const QScriptValue& ignoreEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreEntities(_rayPickUID, ignoreEntities); }
void setIncludeEntities(const QScriptValue& includeEntities) { DependencyManager::get<RayPickScriptingInterface>()->setIncludeEntities(_rayPickUID, includeEntities); }
void setIgnoreOverlays(const QScriptValue& ignoreOverlays) { DependencyManager::get<RayPickScriptingInterface>()->setIgnoreOverlays(_rayPickUID, ignoreOverlays); }

View file

@ -97,6 +97,14 @@ void LaserPointerManager::update() {
}
}
void LaserPointerManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {
QWriteLocker laserLock(_laserPointerLocks[uid].get());
_laserPointers[uid]->setPrecisionPicking(precisionPicking);
}
}
void LaserPointerManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
QReadLocker lock(&_containsLock);
if (_laserPointers.contains(uid)) {

View file

@ -31,6 +31,7 @@ public:
void editRenderState(QUuid uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps);
const RayPickResult getPrevRayPickResult(const QUuid uid);
void setPrecisionPicking(QUuid uid, const bool precisionPicking);
void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);

View file

@ -30,6 +30,7 @@ public slots:
Q_INVOKABLE void setRenderState(QUuid uid, const QString& renderState) { qApp->getLaserPointerManager().setRenderState(uid, renderState.toStdString()); }
Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid) { return qApp->getLaserPointerManager().getPrevRayPickResult(uid); }
Q_INVOKABLE void setPrecisionPicking(QUuid uid, const bool precisionPicking) { qApp->getLaserPointerManager().setPrecisionPicking(uid, precisionPicking); }
Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) { qApp->getLaserPointerManager().setIgnoreEntities(uid, ignoreEntities); }
Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities) { qApp->getLaserPointerManager().setIncludeEntities(uid, includeEntities); }
Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays) { qApp->getLaserPointerManager().setIgnoreOverlays(uid, ignoreOverlays); }

View file

@ -41,12 +41,14 @@ public:
// The key is the Flags
Flags _flags;
RayPickFilter() : _flags(PICK_NOTHING) {}
RayPickFilter() : _flags(getBitMask(PICK_NOTHING)) {}
RayPickFilter(const Flags& flags) : _flags(flags) {}
bool operator== (const RayPickFilter& rhs) const { return _flags == rhs._flags; }
bool operator!= (const RayPickFilter& rhs) const { return _flags != rhs._flags; }
void setFlag(FlagBit flag, bool value) { _flags[flag] = value; }
bool doesPickNothing() const { return _flags[PICK_NOTHING]; }
bool doesPickEntities() const { return _flags[PICK_ENTITIES]; }
bool doesPickOverlays() const { return _flags[PICK_OVERLAYS]; }
@ -61,27 +63,27 @@ public:
// Helpers for RayPickManager
Flags getEntityFlags() const {
Flags toReturn(PICK_ENTITIES);
unsigned int toReturn = getBitMask(PICK_ENTITIES);
if (doesPickInvisible()) {
toReturn |= Flags(PICK_INCLUDE_INVISIBLE);
toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE);
}
if (doesPickNonCollidable()) {
toReturn |= Flags(PICK_INCLUDE_NONCOLLIDABLE);
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
}
return toReturn;
return Flags(toReturn);
}
Flags getOverlayFlags() const {
Flags toReturn(PICK_OVERLAYS);
unsigned int toReturn = getBitMask(PICK_OVERLAYS);
if (doesPickInvisible()) {
toReturn |= Flags(PICK_INCLUDE_INVISIBLE);
toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE);
}
if (doesPickNonCollidable()) {
toReturn |= Flags(PICK_INCLUDE_NONCOLLIDABLE);
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
}
return toReturn;
return Flags(toReturn);
}
Flags getAvatarFlags() const { return Flags(PICK_AVATARS); }
Flags getHUDFlags() const { return Flags(PICK_HUD); }
Flags getAvatarFlags() const { return Flags(getBitMask(PICK_AVATARS)); }
Flags getHUDFlags() const { return Flags(getBitMask(PICK_HUD)); }
static unsigned int getBitMask(FlagBit bit) { return 1 << bit; }
@ -98,10 +100,12 @@ public:
void disable() { _enabled = false; }
const RayPickFilter& getFilter() { return _filter; }
const float& getMaxDistance() { return _maxDistance; }
const bool& isEnabled() { return _enabled; }
float getMaxDistance() { return _maxDistance; }
bool isEnabled() { return _enabled; }
const RayPickResult& getPrevRayPickResult() { return _prevResult; }
void setPrecisionPicking(bool precisionPicking) { _filter.setFlag(RayPickFilter::PICK_COURSE, !precisionPicking); }
void setRayPickResult(const RayPickResult& rayPickResult) { _prevResult = rayPickResult; }
const QVector<EntityItemID>& getIgnoreEntites() { return _ignoreEntities; }

View file

@ -64,10 +64,11 @@ void RayPickManager::update() {
RayToEntityIntersectionResult entityRes;
bool fromCache = true;
bool invisible = rayPick->getFilter().doesPickInvisible();
bool noncollidable = rayPick->getFilter().doesPickNonCollidable();
bool nonCollidable = rayPick->getFilter().doesPickNonCollidable();
RayPickFilter::Flags entityMask = rayPick->getFilter().getEntityFlags();
if (!checkAndCompareCachedResults(rayKey, results, res, entityMask)) {
entityRes = DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(ray, true, rayPick->getIncludeEntites(), rayPick->getIgnoreEntites(), !invisible, !noncollidable);
entityRes = DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCourse(),
rayPick->getIncludeEntites(), rayPick->getIgnoreEntites(), !invisible, !nonCollidable);
fromCache = false;
}
@ -81,10 +82,11 @@ void RayPickManager::update() {
RayToOverlayIntersectionResult overlayRes;
bool fromCache = true;
bool invisible = rayPick->getFilter().doesPickInvisible();
bool noncollidable = rayPick->getFilter().doesPickNonCollidable();
bool nonCollidable = rayPick->getFilter().doesPickNonCollidable();
RayPickFilter::Flags overlayMask = rayPick->getFilter().getOverlayFlags();
if (!checkAndCompareCachedResults(rayKey, results, res, overlayMask)) {
overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, true, rayPick->getIncludeOverlays(), rayPick->getIgnoreOverlays(), !invisible, !noncollidable);
overlayRes = qApp->getOverlays().findRayIntersectionVector(ray, !rayPick->getFilter().doesPickCourse(),
rayPick->getIncludeOverlays(), rayPick->getIgnoreOverlays(), !invisible, !nonCollidable);
fromCache = false;
}
@ -204,6 +206,14 @@ const RayPickResult RayPickManager::getPrevRayPickResult(const QUuid uid) {
return RayPickResult();
}
void RayPickManager::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {
QWriteLocker lock(_rayPickLocks[uid].get());
_rayPicks[uid]->setPrecisionPicking(precisionPicking);
}
}
void RayPickManager::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
QReadLocker containsLock(&_containsLock);
if (_rayPicks.contains(uid)) {

View file

@ -38,6 +38,7 @@ public:
void disableRayPick(const QUuid uid);
const RayPickResult getPrevRayPickResult(const QUuid uid);
void setPrecisionPicking(QUuid uid, const bool precisionPicking);
void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);

View file

@ -82,6 +82,10 @@ RayPickResult RayPickScriptingInterface::getPrevRayPickResult(QUuid uid) {
return qApp->getRayPickManager().getPrevRayPickResult(uid);
}
void RayPickScriptingInterface::setPrecisionPicking(QUuid uid, const bool precisionPicking) {
qApp->getRayPickManager().setPrecisionPicking(uid, precisionPicking);
}
void RayPickScriptingInterface::setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities) {
qApp->getRayPickManager().setIgnoreEntities(uid, ignoreEntities);
}

View file

@ -43,6 +43,7 @@ public slots:
Q_INVOKABLE void removeRayPick(QUuid uid);
Q_INVOKABLE RayPickResult getPrevRayPickResult(QUuid uid);
Q_INVOKABLE void setPrecisionPicking(QUuid uid, const bool precisionPicking);
Q_INVOKABLE void setIgnoreEntities(QUuid uid, const QScriptValue& ignoreEntities);
Q_INVOKABLE void setIncludeEntities(QUuid uid, const QScriptValue& includeEntities);
Q_INVOKABLE void setIgnoreOverlays(QUuid uid, const QScriptValue& ignoreOverlays);
@ -50,7 +51,6 @@ public slots:
Q_INVOKABLE void setIgnoreAvatars(QUuid uid, const QScriptValue& ignoreAvatars);
Q_INVOKABLE void setIncludeAvatars(QUuid uid, const QScriptValue& includeAvatars);
private:
unsigned int PICK_NOTHING() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_NOTHING); }
unsigned int PICK_ENTITIES() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_ENTITIES); }
unsigned int PICK_OVERLAYS() { return RayPickFilter::getBitMask(RayPickFilter::FlagBit::PICK_OVERLAYS); }

View file

@ -264,7 +264,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Circle3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder().withoutCullFace().withUnlit();
if (getAlpha() != 1.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
if (!getIsSolid() || shouldDrawHUDLayer()) {

View file

@ -117,7 +117,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Cube3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder();
if (getAlpha() != 1.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
if (!getIsSolid() || shouldDrawHUDLayer()) {

View file

@ -19,6 +19,7 @@
#include "GeometryUtil.h"
#include "AbstractViewStateInterface.h"
QString const Image3DOverlay::TYPE = "image3d";
@ -58,12 +59,29 @@ void Image3DOverlay::render(RenderArgs* args) {
if (!_isLoaded) {
_isLoaded = true;
_texture = DependencyManager::get<TextureCache>()->getTexture(_url);
_textureIsLoaded = false;
}
if (!_visible || !getParentVisible() || !_texture || !_texture->isLoaded()) {
return;
}
// Once the texture has loaded, check if we need to update the render item because of transparency
if (!_textureIsLoaded && _texture && _texture->getGPUTexture()) {
_textureIsLoaded = true;
bool prevAlphaTexture = _alphaTexture;
_alphaTexture = _texture->getGPUTexture()->getUsage().isAlpha();
if (_alphaTexture != prevAlphaTexture) {
auto itemID = getRenderItemID();
if (render::Item::isValidID(itemID)) {
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
render::Transaction transaction;
transaction.updateItem(itemID);
scene->enqueueTransaction(transaction);
}
}
}
Q_ASSERT(args->_batch);
gpu::Batch* batch = args->_batch;
@ -92,9 +110,9 @@ void Image3DOverlay::render(RenderArgs* args) {
glm::vec2 topLeft(-x, -y);
glm::vec2 bottomRight(x, y);
glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight);
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth,
(fromImage.y() + fromImage.height()) / imageHeight);
glm::vec2 texCoordTopLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight);
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth,
(fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
const float MAX_COLOR = 255.0f;
xColor color = getColor();
@ -126,7 +144,7 @@ const render::ShapeKey Image3DOverlay::getShapeKey() {
if (_emissive || shouldDrawHUDLayer()) {
builder.withUnlit();
}
if (getAlpha() != 1.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
return builder.build();

View file

@ -39,6 +39,7 @@ public:
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
bool isTransparent() override { return Base3DOverlay::isTransparent() || _alphaTexture; }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) override;
@ -48,6 +49,8 @@ public:
private:
QString _url;
NetworkTexturePointer _texture;
bool _textureIsLoaded { false };
bool _alphaTexture { false };
bool _emissive { false };
QRect _fromImage; // where from in the image to sample

View file

@ -13,6 +13,7 @@
#include <GeometryCache.h>
#include <RegisteredMetaTypes.h>
#include "AbstractViewStateInterface.h"
QString const Line3DOverlay::TYPE = "line3d";
@ -149,7 +150,7 @@ void Line3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Line3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder().withOwnPipeline();
if (getAlpha() != 1.0f || _glow > 0.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
return builder.build();
@ -222,9 +223,17 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
auto glow = properties["glow"];
if (glow.isValid()) {
float prevGlow = _glow;
setGlow(glow.toFloat());
if (_glow > 0.0f) {
_alpha = 0.5f;
// Update our payload key if necessary to handle transparency
if ((prevGlow <= 0.0f && _glow > 0.0f) || (prevGlow > 0.0f && _glow <= 0.0f)) {
auto itemID = getRenderItemID();
if (render::Item::isValidID(itemID)) {
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
render::Transaction transaction;
transaction.updateItem(itemID);
scene->enqueueTransaction(transaction);
}
}
}

View file

@ -45,6 +45,7 @@ public:
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
bool isTransparent() override { return Base3DOverlay::isTransparent() || _glow > 0.0f; }
virtual Line3DOverlay* createClone() const override;

View file

@ -59,6 +59,7 @@ public:
bool isLoaded() { return _isLoaded; }
bool getVisible() const { return _visible; }
bool shouldDrawHUDLayer() const { return _drawHUDLayer; }
virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; };
xColor getColor();
float getAlpha();
Anchor getAnchor() const { return _anchor; }

View file

@ -300,7 +300,12 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
}
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
if (QThread::currentThread() != thread()) {
auto thisOverlay = getOverlay(id);
if (!thisOverlay) {
return false;
}
if (!thisOverlay->is3D() && QThread::currentThread() != thread()) {
// NOTE editOverlay can be called very frequently in scripts and can't afford to
// block waiting on the main thread. Additionally, no script actually
// examines the return value and does something useful with it, so use a non-blocking
@ -309,22 +314,15 @@ bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
return true;
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
thisOverlay->setProperties(properties.toMap());
return true;
}
return false;
thisOverlay->setProperties(properties.toMap());
return true;
}
bool Overlays::editOverlays(const QVariant& propertiesById) {
if (QThread::currentThread() != thread()) {
// NOTE see comment on editOverlay for why this is not a blocking call
QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(QVariant, propertiesById));
return true;
}
bool defer2DOverlays = QThread::currentThread() != thread();
QVariantMap map = propertiesById.toMap();
QVariantMap deferrred;
const QVariantMap map = propertiesById.toMap();
bool success = true;
for (const auto& key : map.keys()) {
OverlayID id = OverlayID(key);
@ -333,9 +331,21 @@ bool Overlays::editOverlays(const QVariant& propertiesById) {
success = false;
continue;
}
QVariant properties = map[key];
const QVariant& properties = map[key];
if (defer2DOverlays && !thisOverlay->is3D()) {
deferrred[key] = properties;
continue;
}
thisOverlay->setProperties(properties.toMap());
}
// For 2D/QML overlays, we need to perform the edit on the main thread
if (defer2DOverlays && !deferrred.empty()) {
// NOTE see comment on editOverlay for why this is not a blocking call
QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(QVariant, deferrred));
}
return success;
}

View file

@ -37,7 +37,7 @@ namespace render {
if (std::static_pointer_cast<Base3DOverlay>(overlay)->getDrawInFront()) {
builder.withLayered();
}
if (overlay->getAlphaPulse() != 0.0f || overlay->getAlpha() != 1.0f) {
if (overlay->isTransparent()) {
builder.withTransparent();
}
} else {

View file

@ -110,7 +110,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder().withOwnPipeline();
if (getAlpha() != 1.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
return builder.build();

View file

@ -62,7 +62,7 @@ void Shape3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Shape3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder();
if (getAlpha() != 1.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
if (!getIsSolid() || shouldDrawHUDLayer()) {

View file

@ -59,7 +59,7 @@ void Sphere3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Sphere3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder();
if (getAlpha() != 1.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
if (!getIsSolid() || shouldDrawHUDLayer()) {

View file

@ -143,7 +143,7 @@ void Text3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Text3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder();
if (getAlpha() != 1.0f) {
if (isTransparent()) {
builder.withTranslucent();
}
return builder.build();

View file

@ -318,8 +318,8 @@ void Web3DOverlay::render(RenderArgs* args) {
}
const render::ShapeKey Web3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias();
if (getAlpha() != 1.0f) {
auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias().withOwnPipeline();
if (isTransparent()) {
builder.withTranslucent();
}
return builder.build();

View file

@ -32,6 +32,10 @@
#include "AnimUtil.h"
#include "IKTarget.h"
static int nextRigId = 1;
static std::map<int, Rig*> rigRegistry;
static std::mutex rigRegistryMutex;
static bool isEqual(const glm::vec3& u, const glm::vec3& v) {
const float EPSILON = 0.0001f;
return glm::length(u - v) / glm::length(u) <= EPSILON;
@ -49,6 +53,26 @@ const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 0.9f, 0.0f);
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f);
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f);
Rig::Rig() {
// Ensure thread-safe access to the rigRegistry.
std::lock_guard<std::mutex> guard(rigRegistryMutex);
// Insert this newly allocated rig into the rig registry
_rigId = nextRigId;
rigRegistry[_rigId] = this;
nextRigId++;
}
Rig::~Rig() {
// Ensure thread-safe access to the rigRegstry, but also prevent the rig from being deleted
// while Rig::animationStateHandlerResult is being invoked on a script thread.
std::lock_guard<std::mutex> guard(rigRegistryMutex);
auto iter = rigRegistry.find(_rigId);
if (iter != rigRegistry.end()) {
rigRegistry.erase(iter);
}
}
void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
UserAnimState::ClipNodeEnum clipNodeEnum;
@ -935,8 +959,19 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh
int identifier = data.key();
StateHandler& value = data.value();
QScriptValue& function = value.function;
auto handleResult = [this, identifier](QScriptValue result) { // called in script thread to get the result back to us.
animationStateHandlerResult(identifier, result);
int rigId = _rigId;
auto handleResult = [rigId, identifier](QScriptValue result) { // called in script thread to get the result back to us.
// Hold the rigRegistryMutex to ensure thread-safe access to the rigRegistry, but
// also to prevent the rig from being deleted while this lambda is being executed.
std::lock_guard<std::mutex> guard(rigRegistryMutex);
// if the rig pointer is in the registry, it has not been deleted yet.
auto iter = rigRegistry.find(rigId);
if (iter != rigRegistry.end()) {
Rig* rig = iter->second;
assert(rig);
rig->animationStateHandlerResult(identifier, result);
}
};
// invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture
// the state of _animVars and allow continued changes to _animVars in this thread without conflict.

View file

@ -97,8 +97,8 @@ public:
Hover
};
Rig() {}
virtual ~Rig() {}
Rig();
virtual ~Rig();
void destroyAnimGraph();
@ -372,6 +372,8 @@ protected:
glm::vec3 _prevLeftHandPoleVector { -Vectors::UNIT_Z };
bool _prevLeftHandPoleVectorValid { false };
int _rigId;
};
#endif /* defined(__hifi__Rig__) */

View file

@ -53,7 +53,6 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
_viewState(viewState),
_scriptingServices(scriptingServices),
_displayModelBounds(false),
_dontDoPrecisionPicking(false),
_layeredZones(this)
{
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
@ -428,28 +427,6 @@ void EntityTreeRenderer::deleteReleasedModels() {
}
}
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
RayToEntityIntersectionResult result;
if (_tree) {
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
OctreeElementPointer element;
EntityItemPointer intersectedEntity = NULL;
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction,
entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking,
element, result.distance, result.face, result.surfaceNormal,
(void**)&intersectedEntity, lockType, &result.accurate);
if (result.intersects && intersectedEntity) {
result.entityID = intersectedEntity->getEntityItemID();
result.intersection = ray.origin + (ray.direction * result.distance);
result.entity = intersectedEntity;
}
}
return result;
}
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
@ -530,11 +507,10 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
if (!_tree || _shuttingDown) {
return;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = !_dontDoPrecisionPicking;
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
if (rayPickResult.intersects) {
//qCDebug(entitiesrenderer) << "mousePressEvent over entity:" << rayPickResult.entityID;
@ -579,11 +555,10 @@ void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) {
if (!_tree || _shuttingDown) {
return;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mouseDoublePressEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = !_dontDoPrecisionPicking;
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
if (rayPickResult.intersects) {
//qCDebug(entitiesrenderer) << "mouseDoublePressEvent over entity:" << rayPickResult.entityID;
@ -622,8 +597,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = !_dontDoPrecisionPicking;
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
if (rayPickResult.intersects) {
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
@ -671,14 +645,11 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
if (!_tree || _shuttingDown) {
return;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = false; // for mouse moves we do not do precision picking
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
if (rayPickResult.intersects) {
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID,
pos2D, rayPickResult.intersection,

View file

@ -59,6 +59,10 @@ public:
float getEntityLoadingPriority(const EntityItem& item) const { return _calculateEntityLoadingPriorityFunc(item); }
void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { this->_calculateEntityLoadingPriorityFunc = fn; }
void setMouseRayPickID(QUuid rayPickID) { _mouseRayPickID = rayPickID; }
void setMouseRayPickResultOperator(std::function<RayToEntityIntersectionResult(QUuid)> getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; }
void setSetPrecisionPickingOperator(std::function<void(QUuid, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
void shutdown();
void update();
@ -130,7 +134,7 @@ public slots:
// optional slots that can be wired to menu items
void setDisplayModelBounds(bool value) { _displayModelBounds = value; }
void setDontDoPrecisionPicking(bool value) { _dontDoPrecisionPicking = value; }
void setPrecisionPicking(bool value) { _setPrecisionPickingOperator(_mouseRayPickID, value); }
protected:
virtual OctreePointer createTree() override {
@ -150,10 +154,6 @@ private:
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);
QList<ModelPointer> _releasedModels;
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude = QVector<EntityItemID>(),
const QVector<EntityItemID>& entityIdsToDiscard = QVector<EntityItemID>(), bool visibleOnly=false,
bool collidableOnly = false);
EntityItemID _currentHoverOverEntityID;
EntityItemID _currentClickingOnEntityID;
@ -176,12 +176,15 @@ private:
AbstractViewStateInterface* _viewState;
AbstractScriptingServicesInterface* _scriptingServices;
bool _displayModelBounds;
bool _dontDoPrecisionPicking;
bool _shuttingDown { false };
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
QUuid _mouseRayPickID;
std::function<RayToEntityIntersectionResult(QUuid)> _getPrevRayPickResultOperator;
std::function<void(QUuid, bool)> _setPrecisionPickingOperator;
class LayeredZone {
public:
LayeredZone(std::shared_ptr<ZoneEntityItem> zone, QUuid id, float volume) : zone(zone), id(id), volume(volume) {}

View file

@ -280,24 +280,24 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) {
}
void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
QMutexLocker lock(&_mutex);
QMutexLocker lock(&_dynamicsMutex);
_dynamicsToAdd += dynamic;
}
void EntitySimulation::removeDynamic(const QUuid dynamicID) {
QMutexLocker lock(&_mutex);
QMutexLocker lock(&_dynamicsMutex);
_dynamicsToRemove += dynamicID;
}
void EntitySimulation::removeDynamics(QList<QUuid> dynamicIDsToRemove) {
QMutexLocker lock(&_mutex);
QMutexLocker lock(&_dynamicsMutex);
foreach(QUuid uuid, dynamicIDsToRemove) {
_dynamicsToRemove.insert(uuid);
}
}
void EntitySimulation::applyDynamicChanges() {
QMutexLocker lock(&_mutex);
QMutexLocker lock(&_dynamicsMutex);
_dynamicsToAdd.clear();
_dynamicsToRemove.clear();
}

View file

@ -105,6 +105,7 @@ protected:
SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion
QList<EntityDynamicPointer> _dynamicsToAdd;
QSet<QUuid> _dynamicsToRemove;
QMutex _dynamicsMutex { QMutex::Recursive };
protected:
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)

View file

@ -113,6 +113,18 @@ QString HifiSockAddr::toString() const {
return _address.toString() + ":" + QString::number(_port);
}
bool HifiSockAddr::hasPrivateAddress() const {
// an address is private if it is loopback or falls in any of the RFC1918 address spaces
const QPair<QHostAddress, int> TWENTY_FOUR_BIT_BLOCK = { QHostAddress("10.0.0.0"), 8 };
const QPair<QHostAddress, int> TWENTY_BIT_BLOCK = { QHostAddress("172.16.0.0") , 12 };
const QPair<QHostAddress, int> SIXTEEN_BIT_BLOCK = { QHostAddress("192.168.0.0"), 16 };
return _address.isLoopback()
|| _address.isInSubnet(TWENTY_FOUR_BIT_BLOCK)
|| _address.isInSubnet(TWENTY_BIT_BLOCK)
|| _address.isInSubnet(SIXTEEN_BIT_BLOCK);
}
QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) {
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
return debug.space();

View file

@ -55,6 +55,8 @@ public:
QString toString() const;
bool hasPrivateAddress() const; // checks if the address behind this sock addr is private per RFC 1918
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);

View file

@ -202,12 +202,12 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() {
return *_dtlsSocket;
}
bool LimitedNodeList::isPacketVerified(const udt::Packet& packet) {
bool LimitedNodeList::isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode) {
// We track bandwidth when doing packet verification to avoid needing to do a node lookup
// later when we already do it in packetSourceAndHashMatchAndTrackBandwidth. A node lookup
// incurs a lock, so it is ideal to avoid needing to do it 2+ times for each packet
// received.
return packetVersionMatch(packet) && packetSourceAndHashMatchAndTrackBandwidth(packet);
return packetVersionMatch(packet) && packetSourceAndHashMatchAndTrackBandwidth(packet, sourceNode);
}
bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) {
@ -256,7 +256,7 @@ bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) {
}
}
bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet) {
bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet, Node* sourceNode) {
PacketType headerType = NLPacket::typeInHeader(packet);
@ -298,14 +298,18 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
} else {
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
// figure out which node this is from
SharedNodePointer matchingNode = nodeWithUUID(sourceID);
// check if we were passed a sourceNode hint or if we need to look it up
if (!sourceNode) {
// figure out which node this is from
SharedNodePointer matchingNode = nodeWithUUID(sourceID);
sourceNode = matchingNode.data();
}
if (matchingNode) {
if (sourceNode) {
if (!PacketTypeEnum::getNonVerifiedPackets().contains(headerType)) {
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, matchingNode->getConnectionSecret());
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
// check if the md5 hash in the header matches the hash we would expect
if (packetHeaderHash != expectedHash) {
@ -323,9 +327,9 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
// No matter if this packet is handled or not, we update the timestamp for the last time we heard
// from this sending node
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
sourceNode->setLastHeardMicrostamp(usecTimestampNow());
emit dataReceived(matchingNode->getType(), packet.getPayloadSize());
emit dataReceived(sourceNode->getType(), packet.getPayloadSize());
return true;

View file

@ -286,7 +286,9 @@ public:
void setPacketFilterOperator(udt::PacketFilterOperator filterOperator) { _nodeSocket.setPacketFilterOperator(filterOperator); }
bool packetVersionMatch(const udt::Packet& packet);
bool isPacketVerified(const udt::Packet& packet);
bool isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode = nullptr);
bool isPacketVerified(const udt::Packet& packet) { return isPacketVerifiedWithSource(packet); }
static void makeSTUNRequestPacket(char* stunRequestPacket);
@ -352,7 +354,7 @@ protected:
void setLocalSocket(const HifiSockAddr& sockAddr);
bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet);
bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet, Node* sourceNode = nullptr);
void processSTUNResponse(std::unique_ptr<udt::BasePacket> packet);
void handleNodeKill(const SharedNodePointer& node);

View file

@ -53,8 +53,12 @@ QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
values["permissions_id"] = _id;
if (_groupIDSet) {
values["group_id"] = _groupID;
if (groupRanks.contains(_rankID)) {
if (!_rankID.isNull()) {
values["rank_id"] = _rankID;
}
if (groupRanks.contains(_rankID)) {
values["rank_name"] = groupRanks[_rankID].name;
values["rank_order"] = groupRanks[_rankID].order;
}

View file

@ -348,8 +348,7 @@ void PhysicalEntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
void PhysicalEntitySimulation::applyDynamicChanges() {
QList<EntityDynamicPointer> dynamicsFailedToAdd;
if (_physicsEngine) {
// FIXME put fine grain locking into _physicsEngine
QMutexLocker lock(&_mutex);
QMutexLocker lock(&_dynamicsMutex);
foreach(QUuid dynamicToRemove, _dynamicsToRemove) {
_physicsEngine->removeDynamic(dynamicToRemove);
}
@ -360,9 +359,10 @@ void PhysicalEntitySimulation::applyDynamicChanges() {
}
}
}
// applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd
EntitySimulation::applyDynamicChanges();
}
// applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd
EntitySimulation::applyDynamicChanges();
// put back the ones that couldn't yet be added
foreach (EntityDynamicPointer dynamicFailedToAdd, dynamicsFailedToAdd) {
addDynamic(dynamicFailedToAdd);

View file

@ -1182,7 +1182,7 @@ function MyController(hand) {
this.fullEnd = fullEnd;
this.laserPointer = LaserPointers.createLaserPointer({
joint: (hand == RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS | RayPick.PICK_INCLUDE_NONCOLLIDABLE,
maxDistance: PICK_MAX_DISTANCE,
posOffset: getGrabPointSphereOffset(this.handToController()),
renderStates: renderStates,
@ -1191,7 +1191,7 @@ function MyController(hand) {
});
this.headLaserPointer = LaserPointers.createLaserPointer({
joint: "Avatar",
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS | RayPick.PICK_INCLUDE_NONCOLLIDABLE,
maxDistance: PICK_MAX_DISTANCE,
renderStates: headRenderStates,
faceAvatar: true,

View file

@ -42,6 +42,8 @@
});
function fromQml(message) {
console.debug('tablet-goto::fromQml: message = ', JSON.stringify(message));
var response = {id: message.id, jsonrpc: "2.0"};
switch (message.method) {
case 'request':
@ -98,6 +100,8 @@
button.editProperties({isActive: shouldActivateButton});
wireEventBridge(true);
messagesWaiting(false);
tablet.sendToQml({ method: 'refreshFeeds' })
} else {
shouldActivateButton = false;
onGotoScreen = false;