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

This commit is contained in:
Roxanne Skelly 2019-01-03 13:39:19 -08:00
commit 2b1547b9fa
60 changed files with 2482 additions and 373 deletions

View file

@ -276,6 +276,7 @@ void AssignmentClientMonitor::checkSpares() {
// Spawn or kill children, as needed. If --min or --max weren't specified, allow the child count
// to drift up or down as far as needed.
if (spareCount < 1 || totalCount < _minAssignmentClientForks) {
if (!_maxAssignmentClientForks || totalCount < _maxAssignmentClientForks) {
spawnChildClient();
@ -307,7 +308,7 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
AssignmentClientChildData* childData = nullptr;
if (!matchingNode) {
// The parent only expects to be talking with prorams running on this same machine.
// The parent only expects to be talking with programs running on this same machine.
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
@ -316,9 +317,9 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode(senderID, NodeType::Unassigned,
senderSockAddr, senderSockAddr);
auto childData = std::unique_ptr<AssignmentClientChildData>
auto newChildData = std::unique_ptr<AssignmentClientChildData>
{ new AssignmentClientChildData(Assignment::Type::AllTypes) };
matchingNode->setLinkedData(std::move(childData));
matchingNode->setLinkedData(std::move(newChildData));
} else {
// tell unknown assignment-client child to exit.
qDebug() << "Asking unknown child at" << senderSockAddr << "to exit.";
@ -329,9 +330,8 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
return;
}
}
} else {
childData = dynamic_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
}
childData = dynamic_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
if (childData) {
// update our records about how to reach this child

View file

@ -149,7 +149,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
break;
}
if (traitType == AvatarTraits::AvatarEntity) {
if (traitType == AvatarTraits::AvatarEntity ||
traitType == AvatarTraits::Grab) {
auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID);
if (packetTraitVersion > instanceVersionRef) {

View file

@ -208,6 +208,8 @@
#include "InterfaceParentFinder.h"
#include "ui/OctreeStatsProvider.h"
#include "avatar/GrabManager.h"
#include <GPUIdent.h>
#include <gl/GLHelpers.h>
#include <src/scripting/GooglePolyScriptingInterface.h>
@ -919,6 +921,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<ResourceRequestObserver>();
DependencyManager::set<Keyboard>();
DependencyManager::set<KeyboardScriptingInterface>();
DependencyManager::set<GrabManager>();
return previousSessionCrashed;
}
@ -1078,6 +1081,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
auto nodeList = DependencyManager::get<NodeList>();
nodeList->startThread();
nodeList->setFlagTimeForConnectionStep(true);
// move the AddressManager to the NodeList thread so that domain resets due to domain changes always occur
// before we tell MyAvatar to go to a new location in the new domain
@ -6084,6 +6088,9 @@ void Application::update(float deltaTime) {
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateDialogs(deltaTime); // update various stats dialogs if present
auto grabManager = DependencyManager::get<GrabManager>();
grabManager->simulateGrabs();
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
{

View file

@ -58,6 +58,16 @@ AvatarActionHold::~AvatarActionHold() {
#endif
}
void AvatarActionHold::removeFromOwner() {
auto avatarManager = DependencyManager::get<AvatarManager>();
if (avatarManager) {
auto myAvatar = avatarManager->getMyAvatar();
if (myAvatar) {
myAvatar->removeHoldAction(this);
}
}
}
bool AvatarActionHold::getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
MyCharacterController* controller = myAvatar ? myAvatar->getCharacterController() : nullptr;
@ -143,7 +153,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
ownerEntity->setTransitingWithAvatar(_isTransitingWithAvatar);
}
}
if (holdingAvatar->isMyAvatar()) {
std::shared_ptr<MyAvatar> myAvatar = avatarManager->getMyAvatar();
@ -226,7 +236,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
}
rotation = palmRotation * _relativeRotation;
position = palmPosition + rotation * _relativePosition;
position = palmPosition + palmRotation * _relativePosition;
// update linearVelocity based on offset via _relativePosition;
linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition);
@ -369,8 +379,12 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
hand = _hand;
}
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
holderID = myAvatar->getSessionUUID();
ok = true;
holderID = EntityDynamicInterface::extractStringArgument("hold", arguments, "holderID", ok, false);
if (!ok) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
holderID = myAvatar->getSessionUUID();
}
ok = true;
kinematic = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
@ -417,13 +431,13 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
_kinematicSetVelocity = kinematicSetVelocity;
_ignoreIK = ignoreIK;
_active = true;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setDynamicDataDirty(true);
ownerEntity->setDynamicDataNeedsTransmit(true);
ownerEntity->setDynamicDataNeedsTransmit(true);
ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isActive());
}
});

View file

@ -26,6 +26,8 @@ public:
AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AvatarActionHold();
virtual void removeFromOwner() override;
virtual bool updateArguments(QVariantMap arguments) override;
virtual QVariantMap getArguments() override;

View file

@ -887,3 +887,13 @@ QVariantMap AvatarManager::getPalData(const QStringList& specificAvatarIdentifie
doc.insert("data", palData);
return doc.toVariantMap();
}
void AvatarManager::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators) {
auto avatarMap = getHashCopy();
AvatarHash::iterator itr = avatarMap.begin();
while (itr != avatarMap.end()) {
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
avatar->accumulateGrabPositions(grabAccumulators);
itr++;
}
}

View file

@ -198,6 +198,8 @@ public:
void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction);
void removeDeadAvatarEntities(const SetOfEntities& deadEntities);
void accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators);
public slots:
/**jsdoc
* @function AvatarManager.updateAvatarRenderStatus
@ -215,7 +217,7 @@ private:
void simulateAvatarFades(float deltaTime);
AvatarSharedPointer newSharedAvatar() override;
// called only from the AvatarHashMap thread - cannot be called while this thread holds the
// hash lock, since handleRemovedAvatar needs a write lock on the entity tree and the entity tree
// frequently grabs a read lock on the hash to get a given avatar by ID

View file

@ -0,0 +1,39 @@
//
// GrabManager.cpp
// interface/src/avatar/
//
// Created by Seth Alves on 2018-12-4.
// 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 "GrabManager.h"
void GrabManager::simulateGrabs() {
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
// Update grabbed objects
auto entityTreeRenderer = DependencyManager::get<EntityTreeRenderer>();
auto entityTree = entityTreeRenderer->getTree();
entityTree->withReadLock([&] {
PROFILE_RANGE(simulation, "Grabs");
std::map<QUuid, GrabLocationAccumulator> grabAccumulators;
avatarManager->accumulateGrabPositions(grabAccumulators);
for (auto& accumulatedLocation : grabAccumulators) {
QUuid grabbedThingID = accumulatedLocation.first;
GrabLocationAccumulator& acc = accumulatedLocation.second;
bool success;
SpatiallyNestablePointer grabbedThing = SpatiallyNestable::findByID(grabbedThingID, success);
if (success && grabbedThing) {
glm::vec3 finalPosition = acc.finalizePosition();
glm::quat finalOrientation = acc.finalizeOrientation();
grabbedThing->setTransform(createMatFromQuatAndPos(finalOrientation, finalPosition));
}
}
});
}

View file

@ -0,0 +1,23 @@
//
// GrabManager.h
// interface/src/avatar/
//
// Created by Seth Alves on 2018-12-4.
// 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 <AvatarData.h>
#include <EntityTreeRenderer.h>
#include "AvatarManager.h"
class GrabManager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
void simulateGrabs();
};

View file

@ -746,7 +746,7 @@ void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool ca
void MyAvatar::simulate(float deltaTime) {
PerformanceTimer perfTimer("simulate");
animateScaleChanges(deltaTime);
setFlyingEnabled(getFlyingEnabled());
if (_cauterizationNeedsUpdate) {
@ -820,6 +820,7 @@ void MyAvatar::simulate(float deltaTime) {
// and all of its joints, now update our attachements.
Avatar::simulateAttachments(deltaTime);
relayJointDataToChildren();
updateGrabs();
if (!_skeletonModel->hasSkeleton()) {
// All the simulation that can be done has been done
@ -874,47 +875,12 @@ void MyAvatar::simulate(float deltaTime) {
zoneAllowsFlying = zone->getFlyingAllowed();
collisionlessAllowed = zone->getGhostingAllowed();
}
auto now = usecTimestampNow();
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
MovingEntitiesOperator moveOperator;
bool force = false;
bool iShouldTellServer = true;
forEachDescendant([&](SpatiallyNestablePointer object) {
// if the queryBox has changed, tell the entity-server
if (object->getNestableType() == NestableType::Entity && object->updateQueryAACube()) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
bool success;
AACube newCube = entity->getQueryAACube(success);
if (success) {
moveOperator.addEntityToMoveList(entity, newCube);
}
// send an edit packet to update the entity-server about the queryAABox
if (packetSender && entity->isDomainEntity()) {
EntityItemProperties properties = entity->getProperties();
properties.setQueryAACubeDirty();
properties.setLastEdited(now);
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree,
entity->getID(), properties);
entity->setLastBroadcast(usecTimestampNow());
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
EntityItemPointer entityDescendant = std::dynamic_pointer_cast<EntityItem>(descendant);
if (entityDescendant && entityDescendant->isDomainEntity() && descendant->updateQueryAACube()) {
EntityItemProperties descendantProperties;
descendantProperties.setQueryAACube(descendant->getQueryAACube());
descendantProperties.setLastEdited(now);
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree,
entityDescendant->getID(), descendantProperties);
entityDescendant->setLastBroadcast(now); // for debug/physics status icons
}
});
}
}
entityTree->updateEntityQueryAACube(object, packetSender, force, iShouldTellServer);
});
// also update the position of children in our local octree
if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator");
entityTree->recurseTreeWithOperator(&moveOperator);
}
});
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
_characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled);
@ -4746,3 +4712,50 @@ SpatialParentTree* MyAvatar::getParentTree() const {
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
return entityTree.get();
}
const QUuid MyAvatar::grab(const QUuid& targetID, int parentJointIndex,
glm::vec3 positionalOffset, glm::quat rotationalOffset) {
auto grabID = QUuid::createUuid();
// create a temporary grab object to get grabData
QString hand = "none";
if (parentJointIndex == CONTROLLER_RIGHTHAND_INDEX ||
parentJointIndex == CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX ||
parentJointIndex == FARGRAB_RIGHTHAND_INDEX ||
parentJointIndex == getJointIndex("RightHand")) {
hand = "right";
} else if (parentJointIndex == CONTROLLER_LEFTHAND_INDEX ||
parentJointIndex == CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX ||
parentJointIndex == FARGRAB_LEFTHAND_INDEX ||
parentJointIndex == getJointIndex("LeftHand")) {
hand = "left";
}
Grab tmpGrab(DependencyManager::get<NodeList>()->getSessionUUID(),
targetID, parentJointIndex, hand, positionalOffset, rotationalOffset);
QByteArray grabData = tmpGrab.toByteArray();
bool dataChanged = updateAvatarGrabData(grabID, grabData);
if (dataChanged && _clientTraitsHandler) {
// indicate that the changed data should be sent to the mixer
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::Grab, grabID);
}
return grabID;
}
void MyAvatar::releaseGrab(const QUuid& grabID) {
bool tellHandler { false };
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabData.remove(grabID)) {
_deletedAvatarGrabs.insert(grabID);
tellHandler = true;
}
});
if (tellHandler && _clientTraitsHandler) {
// indicate the deletion of the data to the mixer
_clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::Grab, grabID);
}
}

View file

@ -1170,6 +1170,25 @@ public:
glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); }
/**jsdoc
* Create a new grab.
* @function MyAvatar.grab
* @param {Uuid} targetID - id of grabbed thing
* @param {number} parentJointIndex - avatar joint being used to grab
* @param {Vec3} offset - target's positional offset from joint
* @param {Quat} rotationalOffset - target's rotational offset from joint
* @returns {Uuid} id of the new grab
*/
Q_INVOKABLE const QUuid grab(const QUuid& targetID, int parentJointIndex,
glm::vec3 positionalOffset, glm::quat rotationalOffset);
/**jsdoc
* Release (delete) a grab.
* @function MyAvatar.releaseGrab
* @param {Uuid} grabID - id of grabbed thing
*/
Q_INVOKABLE void releaseGrab(const QUuid& grabID);
public slots:
/**jsdoc

View file

@ -123,7 +123,7 @@ void Text3DOverlay::render(RenderArgs* args) {
glm::vec4 textColor = { toGlm(_color), getTextAlpha() };
// FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase.
_textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f), true);
_textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f));
}
const render::ShapeKey Text3DOverlay::getShapeKey() {

View file

@ -32,6 +32,7 @@
#include <shared/Camera.h>
#include <SoftAttachmentModel.h>
#include <render/TransitionStage.h>
#include <GLMHelpers.h>
#include "ModelEntityItem.h"
#include "RenderableModelEntityItem.h"
@ -481,6 +482,111 @@ void Avatar::removeAvatarEntitiesFromTree() {
}
}
void Avatar::updateGrabs() {
// update the Grabs according to any changes in _avatarGrabData
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabDataChanged) {
foreach (auto grabID, _avatarGrabData.keys()) {
AvatarGrabMap::iterator grabItr = _avatarGrabs.find(grabID);
if (grabItr == _avatarGrabs.end()) {
GrabPointer grab = std::make_shared<Grab>();
grab->fromByteArray(_avatarGrabData.value(grabID));
_avatarGrabs[grabID] = grab;
_changedAvatarGrabs.insert(grabID);
} else {
GrabPointer grab = grabItr.value();
bool changed = grab->fromByteArray(_avatarGrabData.value(grabID));
if (changed) {
_changedAvatarGrabs.insert(grabID);
}
}
}
_avatarGrabDataChanged = false;
}
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
auto entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
EntityEditPacketSender* packetSender = treeRenderer ? treeRenderer->getPacketSender() : nullptr;
auto sessionID = DependencyManager::get<NodeList>()->getSessionUUID();
QMutableSetIterator<QUuid> delItr(_deletedAvatarGrabs);
while (delItr.hasNext()) {
QUuid grabID = delItr.next();
GrabPointer grab = _avatarGrabs[grabID];
if (!grab) {
delItr.remove();
continue;
}
bool success;
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
// only clear this entry from the _deletedAvatarGrabs if we found the entity.
if (success && target) {
bool iShouldTellServer = target->getEditSenderID() == sessionID;
EntityItemPointer entity = std::dynamic_pointer_cast<EntityItem>(target);
if (entity && entity->isAvatarEntity() && (entity->getOwningAvatarID() == sessionID ||
entity->getOwningAvatarID() == AVATAR_SELF_ID)) {
// this is our own avatar-entity, so we always tell the server about the release
iShouldTellServer = true;
}
target->removeGrab(grab);
delItr.remove();
// in case this is the last grab on an entity, we need to shrink the queryAACube and tell the server
// about the final position.
if (entityTree) {
bool force = true;
entityTree->withWriteLock([&] {
entityTree->updateEntityQueryAACube(target, packetSender, force, iShouldTellServer);
});
}
}
_avatarGrabs.remove(grabID);
_changedAvatarGrabs.remove(grabID);
}
QMutableSetIterator<QUuid> changeItr(_changedAvatarGrabs);
while (changeItr.hasNext()) {
QUuid grabID = changeItr.next();
GrabPointer& grab = _avatarGrabs[grabID];
bool success;
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
if (success && target) {
target->addGrab(grab);
// only clear this entry from the _changedAvatarGrabs if we found the entity.
changeItr.remove();
}
}
});
}
void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators) {
// relay avatar's joint position to grabbed target in a way that allows for averaging
_avatarGrabsLock.withReadLock([&] {
foreach (auto grabID, _avatarGrabs.keys()) {
const GrabPointer& grab = _avatarGrabs.value(grabID);
if (!grab || !grab->getActionID().isNull()) {
continue; // the accumulated value isn't used, in this case.
}
glm::vec3 jointTranslation = getAbsoluteJointTranslationInObjectFrame(grab->getParentJointIndex());
glm::quat jointRotation = getAbsoluteJointRotationInObjectFrame(grab->getParentJointIndex());
glm::mat4 jointMat = createMatFromQuatAndPos(jointRotation, jointTranslation);
glm::mat4 offsetMat = createMatFromQuatAndPos(grab->getRotationalOffset(), grab->getPositionalOffset());
glm::mat4 avatarMat = getTransform().getMatrix();
glm::mat4 worldTransform = avatarMat * jointMat * offsetMat;
GrabLocationAccumulator& grabLocationAccumulator = grabAccumulators[grab->getTargetID()];
grabLocationAccumulator.accumulate(extractTranslation(worldTransform), extractRotation(worldTransform));
}
});
}
void Avatar::relayJointDataToChildren() {
forEachChild([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
@ -551,7 +657,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
if (!hasParent()) {
setLocalPosition(_globalPosition);
}
_simulationRate.increment();
if (inView) {
_simulationInViewRate.increment();
@ -617,6 +723,11 @@ void Avatar::simulate(float deltaTime, bool inView) {
updateAvatarEntities();
}
{
PROFILE_RANGE(simulation, "grabs");
updateGrabs();
}
updateFadingStatus();
}
@ -1295,6 +1406,9 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
}
switch (index) {
case NO_JOINT_INDEX: {
return glm::quat();
}
case SENSOR_TO_WORLD_MATRIX_INDEX: {
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
glm::mat4 avatarMatrix = getLocalTransform().getMatrix();
@ -1344,6 +1458,9 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
}
switch (index) {
case NO_JOINT_INDEX: {
return glm::vec3(0.0f);
}
case SENSOR_TO_WORLD_MATRIX_INDEX: {
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
glm::mat4 avatarMatrix = getLocalTransform().getMatrix();

View file

@ -440,6 +440,8 @@ public:
std::shared_ptr<AvatarTransit> getTransit() { return std::make_shared<AvatarTransit>(_transit); };
AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config);
void accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators);
signals:
void targetScaleChanged(float targetScale);
@ -542,6 +544,7 @@ protected:
// protected methods...
bool isLookingAtMe(AvatarSharedPointer avatar) const;
void updateGrabs();
void relayJointDataToChildren();
void fade(render::Transaction& transaction, render::Transition::Type type);
@ -628,6 +631,8 @@ protected:
static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs);
AvatarGrabMap _avatarGrabs;
};
#endif // hifi_Avatar_h

View file

@ -1893,42 +1893,96 @@ qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice
return bytesWritten;
}
qint64 AvatarData::packAvatarEntityTraitInstance(AvatarTraits::TraitType traitType,
AvatarTraits::TraitInstanceID traitInstanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) {
qint64 bytesWritten = 0;
// grab a read lock on the avatar entities and check for entity data for the given ID
QByteArray entityBinaryData;
_avatarEntitiesLock.withReadLock([this, &entityBinaryData, &traitInstanceID] {
if (_avatarEntityData.contains(traitInstanceID)) {
entityBinaryData = _avatarEntityData[traitInstanceID];
}
});
if (entityBinaryData.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) {
qWarning() << "Refusing to pack instanced trait" << traitType << "of size" << entityBinaryData.size()
<< "bytes since it exceeds the maximum size " << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes";
return 0;
}
bytesWritten += destination.writePrimitive(traitType);
if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) {
bytesWritten += destination.writePrimitive(traitVersion);
}
bytesWritten += destination.write(traitInstanceID.toRfc4122());
if (!entityBinaryData.isNull()) {
AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size();
bytesWritten += destination.writePrimitive(entityBinarySize);
bytesWritten += destination.write(entityBinaryData);
} else {
bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE);
}
return bytesWritten;
}
qint64 AvatarData::packGrabTraitInstance(AvatarTraits::TraitType traitType,
AvatarTraits::TraitInstanceID traitInstanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) {
qint64 bytesWritten = 0;
// grab a read lock on the avatar grabs and check for grab data for the given ID
QByteArray grabBinaryData;
_avatarGrabsLock.withReadLock([this, &grabBinaryData, &traitInstanceID] {
if (_avatarGrabData.contains(traitInstanceID)) {
grabBinaryData = _avatarGrabData[traitInstanceID];
}
});
if (grabBinaryData.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) {
qWarning() << "Refusing to pack instanced trait" << traitType << "of size" << grabBinaryData.size()
<< "bytes since it exceeds the maximum size " << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes";
return 0;
}
bytesWritten += destination.writePrimitive(traitType);
if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) {
bytesWritten += destination.writePrimitive(traitVersion);
}
bytesWritten += destination.write(traitInstanceID.toRfc4122());
if (!grabBinaryData.isNull()) {
AvatarTraits::TraitWireSize grabBinarySize = grabBinaryData.size();
bytesWritten += destination.writePrimitive(grabBinarySize);
bytesWritten += destination.write(grabBinaryData);
} else {
bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE);
}
return bytesWritten;
}
qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) {
qint64 bytesWritten = 0;
if (traitType == AvatarTraits::AvatarEntity) {
// grab a read lock on the avatar entities and check for entity data for the given ID
QByteArray entityBinaryData;
_avatarEntitiesLock.withReadLock([this, &entityBinaryData, &traitInstanceID] {
if (_avatarEntityData.contains(traitInstanceID)) {
entityBinaryData = _avatarEntityData[traitInstanceID];
}
});
if (entityBinaryData.size() > AvatarTraits::MAXIMUM_TRAIT_SIZE) {
qWarning() << "Refusing to pack instanced trait" << traitType << "of size" << entityBinaryData.size()
<< "bytes since it exceeds the maximum size " << AvatarTraits::MAXIMUM_TRAIT_SIZE << "bytes";
return 0;
}
bytesWritten += destination.writePrimitive(traitType);
if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) {
bytesWritten += destination.writePrimitive(traitVersion);
}
bytesWritten += destination.write(traitInstanceID.toRfc4122());
if (!entityBinaryData.isNull()) {
AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size();
bytesWritten += destination.writePrimitive(entityBinarySize);
bytesWritten += destination.write(entityBinaryData);
} else {
bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE);
}
packAvatarEntityTraitInstance(traitType, traitInstanceID, destination, traitVersion);
} else if (traitType == AvatarTraits::Grab) {
packGrabTraitInstance(traitType, traitInstanceID, destination, traitVersion);
}
return bytesWritten;
@ -1940,6 +1994,9 @@ void AvatarData::prepareResetTraitInstances() {
foreach (auto entityID, _avatarEntityData.keys()) {
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
}
foreach (auto grabID, _avatarGrabData.keys()) {
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::Grab, grabID);
}
});
}
}
@ -1956,12 +2013,16 @@ void AvatarData::processTraitInstance(AvatarTraits::TraitType traitType,
AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) {
if (traitType == AvatarTraits::AvatarEntity) {
updateAvatarEntity(instanceID, traitBinaryData);
} else if (traitType == AvatarTraits::Grab) {
updateAvatarGrabData(instanceID, traitBinaryData);
}
}
void AvatarData::processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) {
if (traitType == AvatarTraits::AvatarEntity) {
clearAvatarEntity(instanceID);
} else if (traitType == AvatarTraits::Grab) {
clearAvatarGrabData(instanceID);
}
}
@ -2909,3 +2970,38 @@ AABox AvatarData::getDefaultBubbleBox() const {
bubbleBox.translate(_globalPosition);
return bubbleBox;
}
bool AvatarData::updateAvatarGrabData(const QUuid& grabID, const QByteArray& grabData) {
bool changed { false };
_avatarGrabsLock.withWriteLock([&] {
AvatarGrabDataMap::iterator itr = _avatarGrabData.find(grabID);
if (itr == _avatarGrabData.end()) {
// create a new one
if (_avatarGrabData.size() < MAX_NUM_AVATAR_GRABS) {
_avatarGrabData.insert(grabID, grabData);
_avatarGrabDataChanged = true;
changed = true;
} else {
qCWarning(avatars) << "Can't create more grabs on avatar, limit reached.";
}
} else {
// update an existing one
if (itr.value() != grabData) {
itr.value() = grabData;
_avatarGrabDataChanged = true;
changed = true;
}
}
});
return changed;
}
void AvatarData::clearAvatarGrabData(const QUuid& grabID) {
_avatarGrabsLock.withWriteLock([&] {
if (_avatarGrabData.remove(grabID)) {
_avatarGrabDataChanged = true;
_deletedAvatarGrabs.insert(grabID);
}
});
}

View file

@ -54,15 +54,21 @@
#include "AvatarTraits.h"
#include "HeadData.h"
#include "PathUtils.h"
#include "Grab.h"
#include <graphics/Material.h>
using AvatarSharedPointer = std::shared_ptr<AvatarData>;
using AvatarWeakPointer = std::weak_ptr<AvatarData>;
using AvatarHash = QHash<QUuid, AvatarSharedPointer>;
using AvatarEntityMap = QMap<QUuid, QByteArray>;
using AvatarEntityIDs = QSet<QUuid>;
using AvatarGrabDataMap = QMap<QUuid, QByteArray>;
using AvatarGrabIDs = QSet<QUuid>;
using AvatarGrabMap = QMap<QUuid, GrabPointer>;
using AvatarDataSequenceNumber = uint16_t;
// avatar motion behaviors
@ -1342,6 +1348,13 @@ protected:
bool hasParent() const { return !getParentID().isNull(); }
bool hasFaceTracker() const { return _headData ? _headData->_isFaceTrackerConnected : false; }
qint64 packAvatarEntityTraitInstance(AvatarTraits::TraitType traitType,
AvatarTraits::TraitInstanceID traitInstanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion);
qint64 packGrabTraitInstance(AvatarTraits::TraitType traitType,
AvatarTraits::TraitInstanceID traitInstanceID,
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion);
// isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "master"
// Audio Mixer that the replicated avatar is connected to.
bool _isReplicated{ false };
@ -1452,6 +1465,12 @@ protected:
AvatarEntityMap _avatarEntityData;
bool _avatarEntityDataChanged { false };
mutable ReadWriteLockable _avatarGrabsLock;
AvatarGrabDataMap _avatarGrabData;
bool _avatarGrabDataChanged { false }; // by network
AvatarGrabIDs _changedAvatarGrabs; // updated grab IDs -- changes needed to entities or physics
AvatarGrabIDs _deletedAvatarGrabs; // deleted grab IDs -- changes needed to entities or physics
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _controllerLeftHandMatrixCache { glm::mat4() };
@ -1511,6 +1530,9 @@ protected:
f(index);
}
bool updateAvatarGrabData(const QUuid& grabID, const QByteArray& grabData);
void clearAvatarGrabData(const QUuid& grabID);
private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
static QUrl _defaultFullAvatarModelUrl;
@ -1614,6 +1636,7 @@ QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEnt
void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value);
// faux joint indexes (-1 means invalid)
const int NO_JOINT_INDEX = 65535; // -1
const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; // -2
const int CONTROLLER_RIGHTHAND_INDEX = 65533; // -3
const int CONTROLLER_LEFTHAND_INDEX = 65532; // -4
@ -1626,5 +1649,6 @@ const int FARGRAB_MOUSE_INDEX = 65526; // -10
const int LOWEST_PSEUDO_JOINT_INDEX = 65526;
const int MAX_NUM_AVATAR_GRABS = 6;
#endif // hifi_AvatarData_h

View file

@ -24,6 +24,7 @@ namespace AvatarTraits {
SkeletonModelURL,
FirstInstancedTrait,
AvatarEntity = FirstInstancedTrait,
Grab,
TotalTraitTypes
};

View file

@ -32,6 +32,7 @@
#include <ScriptEngine.h>
#include <EntitySimulation.h>
#include <ZoneRenderer.h>
#include <PhysicalEntitySimulation.h>
#include "EntitiesRendererLogging.h"
#include "RenderableEntityItem.h"
@ -1250,3 +1251,11 @@ void EntityTreeRenderer::onEntityChanged(const EntityItemID& id) {
_changedEntities.insert(id);
});
}
EntityEditPacketSender* EntityTreeRenderer::getPacketSender() {
EntityTreePointer tree = getTree();
EntitySimulationPointer simulation = tree ? tree->getSimulation() : nullptr;
PhysicalEntitySimulationPointer peSimulation = std::static_pointer_cast<PhysicalEntitySimulation>(simulation);
EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr;
return packetSender;
}

View file

@ -120,6 +120,8 @@ public:
static void setGetAvatarUpOperator(std::function<glm::vec3()> getAvatarUpOperator) { _getAvatarUpOperator = getAvatarUpOperator; }
static glm::vec3 getAvatarUp() { return _getAvatarUpOperator(); }
EntityEditPacketSender* getPacketSender();
signals:
void enterEntity(const EntityItemID& entityItemID);
void leaveEntity(const EntityItemID& entityItemID);

View file

@ -131,21 +131,6 @@ bool ShapeEntityRenderer::isTransparent() const {
return Parent::isTransparent();
}
ItemKey ShapeEntityRenderer::getKey() {
ItemKey::Builder builder;
builder.withTypeShape().withTypeMeta().withTagBits(getTagMask());
withReadLock([&] {
if (isTransparent()) {
builder.withTransparent();
} else if (_canCastShadow) {
builder.withShadowCaster();
}
});
return builder.build();
}
bool ShapeEntityRenderer::useMaterialPipeline() const {
bool proceduralReady = resultWithReadLock<bool>([&] {
return _procedural.isReady();

View file

@ -25,7 +25,6 @@ public:
virtual scriptable::ScriptableModelBase getScriptableModel() override;
protected:
ItemKey getKey() override;
ShapeKey getShapeKey() override;
private:

View file

@ -27,7 +27,10 @@ static const int FIXED_FONT_POINT_SIZE = 40;
TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) :
Parent(entity),
_textRenderer(TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f)) {
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
_geometryID = geometryCache->allocateID();
}
}
TextEntityRenderer::~TextEntityRenderer() {
@ -37,6 +40,18 @@ TextEntityRenderer::~TextEntityRenderer() {
}
}
bool TextEntityRenderer::isTransparent() const {
return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f;
}
ShapeKey TextEntityRenderer::getShapeKey() {
auto builder = render::ShapeKey::Builder().withOwnPipeline();
if (isTransparent()) {
builder.withTranslucent();
}
return builder.build();
}
bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
if (_text != entity->getText()) {
return true;
@ -50,10 +65,18 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return true;
}
if (_textAlpha != entity->getTextAlpha()) {
return true;
}
if (_backgroundColor != toGlm(entity->getBackgroundColor())) {
return true;
}
if (_backgroundAlpha != entity->getBackgroundAlpha()) {
return true;
}
if (_dimensions != entity->getScaledDimensions()) {
return true;
}
@ -61,6 +84,23 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
if (_billboardMode != entity->getBillboardMode()) {
return true;
}
if (_leftMargin != entity->getLeftMargin()) {
return true;
}
if (_rightMargin != entity->getRightMargin()) {
return true;
}
if (_topMargin != entity->getTopMargin()) {
return true;
}
if (_bottomMargin != entity->getBottomMargin()) {
return true;
}
return false;
}
@ -76,14 +116,19 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
}
void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
_textColor = toGlm(entity->getTextColor());
_backgroundColor = toGlm(entity->getBackgroundColor());
_billboardMode = entity->getBillboardMode();
_lineHeight = entity->getLineHeight();
_text = entity->getText();
_lineHeight = entity->getLineHeight();
_textColor = toGlm(entity->getTextColor());
_textAlpha = entity->getTextAlpha();
_backgroundColor = toGlm(entity->getBackgroundColor());
_backgroundAlpha = entity->getBackgroundAlpha();
_billboardMode = entity->getBillboardMode();
_leftMargin = entity->getLeftMargin();
_rightMargin = entity->getRightMargin();
_topMargin = entity->getTopMargin();
_bottomMargin = entity->getBottomMargin();
}
void TextEntityRenderer::doRender(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
@ -93,17 +138,16 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
modelTransform = _renderTransform;
dimensions = _dimensions;
});
static const float SLIGHTLY_BEHIND = -0.005f;
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
bool transparent = fadeRatio < 1.0f;
glm::vec4 textColor = glm::vec4(_textColor, fadeRatio);
glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio);
glm::vec4 textColor = glm::vec4(_textColor, fadeRatio * _textAlpha);
glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
// Render background
static const float SLIGHTLY_BEHIND = -0.005f;
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
@ -135,18 +179,15 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
batch.setModelTransform(transformToTopLeft);
auto geometryCache = DependencyManager::get<GeometryCache>();
if (!_geometryID) {
_geometryID = geometryCache->allocateID();
}
geometryCache->bindSimpleProgram(batch, false, transparent, false, false, false);
geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false);
geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID);
// FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase.
float scale = _lineHeight / _textRenderer->getFontSize();
transformToTopLeft.setScale(scale); // Scale to have the correct line height
batch.setModelTransform(transformToTopLeft);
float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight;
glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin,
dimensions.y - 2.0f * topMargin);
_textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale);
glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin),
dimensions.y - (_topMargin + _bottomMargin));
_textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale);
}

View file

@ -25,19 +25,33 @@ class TextEntityRenderer : public TypedEntityRenderer<TextEntityItem> {
public:
TextEntityRenderer(const EntityItemPointer& entity);
~TextEntityRenderer();
bool isTransparent() const override;
ShapeKey getShapeKey() override;
private:
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
int _geometryID{ 0 };
std::shared_ptr<TextRenderer3D> _textRenderer;
BillboardMode _billboardMode;
glm::vec3 _dimensions;
glm::vec3 _textColor;
glm::vec3 _backgroundColor;
QString _text;
float _lineHeight;
glm::vec3 _textColor;
float _textAlpha;
glm::vec3 _backgroundColor;
float _backgroundAlpha;
float _leftMargin;
float _rightMargin;
float _topMargin;
float _bottomMargin;
BillboardMode _billboardMode;
glm::vec3 _dimensions;
};
} }

View file

@ -50,6 +50,8 @@ public:
const QUuid& getID() const { return _id; }
EntityDynamicType getType() const { return _type; }
virtual void removeFromOwner() { }
virtual void remapIDs(QHash<EntityItemID, EntityItemID>& map) = 0;
virtual bool isAction() const { return false; }

View file

@ -31,6 +31,8 @@
#include <SharedUtil.h> // usecTimestampNow()
#include <LogHandler.h>
#include <Extents.h>
#include <QVariantGLM.h>
#include <Grab.h>
#include "EntityScriptingInterface.h"
#include "EntitiesLogging.h"
@ -38,6 +40,7 @@
#include "EntitySimulation.h"
#include "EntityDynamicFactoryInterface.h"
Q_DECLARE_METATYPE(EntityItemPointer);
int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>();
@ -1647,7 +1650,10 @@ AACube EntityItem::getQueryAACube(bool& success) const {
}
bool EntityItem::shouldPuffQueryAACube() const {
return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent();
bool hasGrabs = _grabsLock.resultWithReadLock<bool>([&] {
return _grabs.count() > 0;
});
return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent() || hasGrabs;
}
// TODO: get rid of all users of this function...
@ -2090,6 +2096,35 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin
return result;
}
void EntityItem::enableNoBootstrap() {
if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
_flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
forEachDescendant([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
}
});
}
}
void EntityItem::disableNoBootstrap() {
if (!stillHasGrabActions()) {
_flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
forEachDescendant([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
}
});
}
}
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action) {
assert(action);
assert(simulation);
@ -2111,17 +2146,7 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn
auto actionType = action->getType();
if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) {
if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
_flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
forEachDescendant([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
}
});
}
enableNoBootstrap();
}
} else {
qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed";
@ -2202,16 +2227,8 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
action->setIsMine(false);
_objectActions.remove(actionID);
if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) {
_flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
forEachDescendant([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
}
});
if (removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) {
disableNoBootstrap();
} else {
// NO-OP: we assume SPECIAL_FLAGS_NO_BOOTSTRAPPING bits and collision group are correct
// because they should have been set correctly when the action was added
@ -2448,6 +2465,14 @@ bool EntityItem::shouldSuppressLocationEdits() const {
i++;
}
i = _grabActions.begin();
while (i != _grabActions.end()) {
if (i.value()->shouldSuppressLocationEdits()) {
return true;
}
i++;
}
// if any of the ancestors are MyAvatar, suppress
return isChildOfMyAvatar();
}
@ -2455,13 +2480,22 @@ bool EntityItem::shouldSuppressLocationEdits() const {
QList<EntityDynamicPointer> EntityItem::getActionsOfType(EntityDynamicType typeToGet) const {
QList<EntityDynamicPointer> result;
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
for (QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
i != _objectActions.end();
i++) {
EntityDynamicPointer action = i.value();
if (action->getType() == typeToGet && action->isActive()) {
result += action;
}
}
for (QHash<QUuid, EntityDynamicPointer>::const_iterator i = _grabActions.begin();
i != _grabActions.end();
i++) {
EntityDynamicPointer action = i.value();
if (action->getType() == typeToGet && action->isActive()) {
result += action;
}
i++;
}
return result;
@ -3281,4 +3315,67 @@ void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properti
bool EntityItem::isWearable() const {
return isVisible() && (getParentID() == DependencyManager::get<NodeList>()->getSessionUUID() || getParentID() == AVATAR_SELF_ID);
}
}
void EntityItem::addGrab(GrabPointer grab) {
enableNoBootstrap();
SpatiallyNestable::addGrab(grab);
if (getDynamic() && getParentID().isNull()) {
EntityTreePointer entityTree = getTree();
assert(entityTree);
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
assert(simulation);
auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
QUuid actionID = QUuid::createUuid();
EntityDynamicType dynamicType;
QVariantMap arguments;
int grabParentJointIndex =grab->getParentJointIndex();
if (grabParentJointIndex == FARGRAB_RIGHTHAND_INDEX || grabParentJointIndex == FARGRAB_LEFTHAND_INDEX) {
// add a far-grab action
dynamicType = DYNAMIC_TYPE_FAR_GRAB;
arguments["otherID"] = grab->getOwnerID();
arguments["otherJointIndex"] = grabParentJointIndex;
arguments["targetPosition"] = vec3ToQMap(grab->getPositionalOffset());
arguments["targetRotation"] = quatToQMap(grab->getRotationalOffset());
arguments["linearTimeScale"] = 0.05;
arguments["angularTimeScale"] = 0.05;
} else {
// add a near-grab action
dynamicType = DYNAMIC_TYPE_HOLD;
arguments["holderID"] = grab->getOwnerID();
arguments["hand"] = grab->getHand();
arguments["timeScale"] = 0.05;
arguments["relativePosition"] = vec3ToQMap(grab->getPositionalOffset());
arguments["relativeRotation"] = quatToQMap(grab->getRotationalOffset());
arguments["kinematic"] = _grabProperties.getGrabKinematic();
arguments["kinematicSetVelocity"] = true;
arguments["ignoreIK"] = _grabProperties.getGrabFollowsController();
}
EntityDynamicPointer action = actionFactory->factory(dynamicType, actionID, getThisPointer(), arguments);
grab->setActionID(actionID);
_grabActions[actionID] = action;
simulation->addDynamic(action);
}
}
void EntityItem::removeGrab(GrabPointer grab) {
SpatiallyNestable::removeGrab(grab);
QUuid actionID = grab->getActionID();
if (!actionID.isNull()) {
EntityDynamicPointer action = _grabActions.value(actionID);
if (action) {
_grabActions.remove(actionID);
EntityTreePointer entityTree = getTree();
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
if (simulation) {
action->removeFromSimulation(simulation);
action->removeFromOwner();
}
}
}
disableNoBootstrap();
}

View file

@ -51,7 +51,6 @@ typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { };
@ -554,6 +553,9 @@ public:
void prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority);
virtual void addGrab(GrabPointer grab) override;
virtual void removeGrab(GrabPointer grab) override;
signals:
void requestRenderUpdate();
void spaceUpdate(std::pair<int32_t, glm::vec4> data);
@ -668,6 +670,9 @@ protected:
bool _simulated { false }; // set by EntitySimulation
bool _visuallyReady { true };
void enableNoBootstrap();
void disableNoBootstrap();
bool addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action);
bool removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation = nullptr);
void deserializeActionsInternal();
@ -734,6 +739,8 @@ protected:
GrabPropertyGroup _grabProperties;
QHash<QUuid, EntityDynamicPointer> _grabActions;
private:
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;

View file

@ -443,6 +443,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_MAJOR_GRID_EVERY, majorGridEvery);
CHECK_PROPERTY_CHANGE(PROP_MINOR_GRID_EVERY, minorGridEvery);
CHECK_PROPERTY_CHANGE(PROP_TEXT_ALPHA, textAlpha);
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_ALPHA, backgroundAlpha);
CHECK_PROPERTY_CHANGE(PROP_LEFT_MARGIN, leftMargin);
CHECK_PROPERTY_CHANGE(PROP_RIGHT_MARGIN, rightMargin);
CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin);
CHECK_PROPERTY_CHANGE(PROP_BOTTOM_MARGIN, bottomMargin);
// Certifiable Properties
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
CHECK_PROPERTY_CHANGE(PROP_ITEM_DESCRIPTION, itemDescription);
@ -1141,11 +1148,17 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* created using <code>\n</code>. Overflowing lines are not displayed.
* @property {number} lineHeight=0.1 - The height of each line of text (thus determining the font size).
* @property {Color} textColor=255,255,255 - The color of the text.
* @property {number} textAlpha=1.0 - The text alpha.
* @property {Color} backgroundColor=0,0,0 - The color of the background rectangle.
* @property {number} backgroundAlpha=1.0 - The background alpha.
* @property {BillboardMode} billboardMode="none" - If <code>"none"</code>, the entity is not billboarded. If <code>"yaw"</code>, the entity will be
* oriented to follow your camera around the y-axis. If <code>"full"</code> the entity will be oriented to face your camera. The following deprecated
* behavior is also supported: you can also set <code>"faceCamera"</code> to <code>true</code> to set <code>billboardMode</code> to "yaw", and you can set
* <code>"isFacingAvatar"</code> to <code>true</code> to set <code>billboardMode</code> to "full". Setting either to <code>false</code> sets the mode to "none"
* @property {number} leftMargin=0.0 - The left margin, in meters.
* @property {number} rightMargin=0.0 - The right margin, in meters.
* @property {number} topMargin=0.0 - The top margin, in meters.
* @property {number} bottomMargin=0.0 - The bottom margin, in meters.
* @example <caption>Create a text entity.</caption>
* var text = Entities.addEntity({
* type: "Text",
@ -1503,8 +1516,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_ALPHA, textAlpha);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor(), u8vec3Color);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BACKGROUND_ALPHA, backgroundAlpha);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LEFT_MARGIN, leftMargin);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RIGHT_MARGIN, rightMargin);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TOP_MARGIN, topMargin);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BOTTOM_MARGIN, bottomMargin);
}
// Zones only
@ -1858,6 +1877,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(majorGridEvery, uint32_t, setMajorGridEvery);
COPY_PROPERTY_FROM_QSCRIPTVALUE(minorGridEvery, float, setMinorGridEvery);
COPY_PROPERTY_FROM_QSCRIPTVALUE(textAlpha, float, setTextAlpha);
COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundAlpha, float, setBackgroundAlpha);
COPY_PROPERTY_FROM_QSCRIPTVALUE(leftMargin, float, setLeftMargin);
COPY_PROPERTY_FROM_QSCRIPTVALUE(rightMargin, float, setRightMargin);
COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin);
COPY_PROPERTY_FROM_QSCRIPTVALUE(bottomMargin, float, setBottomMargin);
if (!honorReadOnly) {
// this is used by the json reader to set things that we don't want javascript to able to affect.
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(created, QDateTime, setCreated, [this]() {
@ -2046,6 +2072,13 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(majorGridEvery);
COPY_PROPERTY_IF_CHANGED(minorGridEvery);
COPY_PROPERTY_IF_CHANGED(textAlpha);
COPY_PROPERTY_IF_CHANGED(backgroundAlpha);
COPY_PROPERTY_IF_CHANGED(leftMargin);
COPY_PROPERTY_IF_CHANGED(rightMargin);
COPY_PROPERTY_IF_CHANGED(topMargin);
COPY_PROPERTY_IF_CHANGED(bottomMargin);
// Certifiable Properties
COPY_PROPERTY_IF_CHANGED(itemName);
COPY_PROPERTY_IF_CHANGED(itemDescription);
@ -2386,6 +2419,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController);
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable);
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable);
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_DELEGATE_TO_PARENT, Grab, grab, GrabDelegateToParent, grabDelegateToParent);
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab,
EquippableLeftPosition, equippableLeftPosition);
ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab,
@ -2410,6 +2444,15 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float);
ADD_PROPERTY_TO_MAP(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float);
ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float);
ADD_PROPERTY_TO_MAP(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float);
ADD_PROPERTY_TO_MAP(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float);
ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float);
ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float);
ADD_PROPERTY_TO_MAP(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid);
// FIXME - these are not yet handled
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64);
@ -2572,8 +2615,14 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText());
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight());
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, properties.getTextColor());
APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, properties.getTextAlpha());
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, properties.getBackgroundColor());
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, properties.getBackgroundAlpha());
APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode());
APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, properties.getLeftMargin());
APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, properties.getRightMargin());
APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, properties.getTopMargin());
APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, properties.getBottomMargin());
}
if (properties.getType() == EntityTypes::Model) {
@ -2998,8 +3047,14 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_ALPHA, float, setTextAlpha);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, u8vec3Color, setBackgroundColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LEFT_MARGIN, float, setLeftMargin);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RIGHT_MARGIN, float, setRightMargin);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TOP_MARGIN, float, setTopMargin);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BOTTOM_MARGIN, float, setBottomMargin);
}
if (properties.getType() == EntityTypes::Model) {
@ -3508,6 +3563,13 @@ void EntityItemProperties::markAllChanged() {
_followCameraChanged = true;
_majorGridEveryChanged = true;
_minorGridEveryChanged = true;
_textAlphaChanged = true;
_backgroundAlphaChanged = true;
_leftMarginChanged = true;
_rightMarginChanged = true;
_topMarginChanged = true;
_bottomMarginChanged = true;
}
// The minimum bounding box for the entity.
@ -3535,6 +3597,18 @@ bool EntityItemProperties::hasTransformOrVelocityChanges() const {
|| _accelerationChanged;
}
void EntityItemProperties::clearTransformOrVelocityChanges() {
_positionChanged = false;
_localPositionChanged = false;
_rotationChanged = false;
_localRotationChanged = false;
_velocityChanged = false;
_localVelocityChanged = false;
_angularVelocityChanged = false;
_localAngularVelocityChanged = false;
_accelerationChanged = false;
}
bool EntityItemProperties::hasMiscPhysicsChanges() const {
return _gravityChanged || _dimensionsChanged || _densityChanged || _frictionChanged
|| _restitutionChanged || _dampingChanged || _angularDampingChanged || _registrationPointChanged ||
@ -3545,6 +3619,7 @@ bool EntityItemProperties::hasSimulationRestrictedChanges() const {
return _positionChanged || _localPositionChanged
|| _rotationChanged || _localRotationChanged
|| _velocityChanged || _localVelocityChanged
|| _localDimensionsChanged || _dimensionsChanged
|| _angularVelocityChanged || _localAngularVelocityChanged
|| _accelerationChanged
|| _parentIDChanged || _parentJointIndexChanged;
@ -4074,6 +4149,25 @@ QList<QString> EntityItemProperties::listChangedProperties() {
out += "minorGridEvery";
}
if (textAlphaChanged()) {
out += "textAlpha";
}
if (backgroundAlphaChanged()) {
out += "backgroundAlpha";
}
if (leftMarginChanged()) {
out += "leftMargin";
}
if (rightMarginChanged()) {
out += "rightMargin";
}
if (topMarginChanged()) {
out += "topMargin";
}
if (bottomMarginChanged()) {
out += "bottomMargin";
}
getAnimation().listChangedProperties(out);
getKeyLight().listChangedProperties(out);
getAmbientLight().listChangedProperties(out);
@ -4271,4 +4365,4 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID
setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT);
setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC);
setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY);
}
}

View file

@ -128,52 +128,60 @@ public:
// type _foo { value };
// bool _fooChanged { false };
// Core Properties
DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool, ENTITY_ITEM_DEFAULT_VISIBLE);
DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW);
DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME);
DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED);
DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA);
DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, "");
DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, "");
DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3);
DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3, ENTITY_ITEM_DEFAULT_DIMENSIONS);
DEFINE_PROPERTY_REF(PROP_ROTATION, Rotation, rotation, glm::quat, ENTITY_ITEM_DEFAULT_ROTATION);
DEFINE_PROPERTY_REF(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT);
DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64, UNKNOWN_CREATED_TIME);
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN);
DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW);
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup);
// Physics
DEFINE_PROPERTY(PROP_DENSITY, Density, density, float, ENTITY_ITEM_DEFAULT_DENSITY);
DEFINE_PROPERTY_REF(PROP_VELOCITY, Velocity, velocity, glm::vec3, ENTITY_ITEM_DEFAULT_VELOCITY);
DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY);
DEFINE_PROPERTY_REF(PROP_GRAVITY, Gravity, gravity, glm::vec3, ENTITY_ITEM_DEFAULT_GRAVITY);
DEFINE_PROPERTY_REF(PROP_ACCELERATION, Acceleration, acceleration, glm::vec3, ENTITY_ITEM_DEFAULT_ACCELERATION);
DEFINE_PROPERTY(PROP_DAMPING, Damping, damping, float, ENTITY_ITEM_DEFAULT_DAMPING);
DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING);
DEFINE_PROPERTY(PROP_RESTITUTION, Restitution, restitution, float, ENTITY_ITEM_DEFAULT_RESTITUTION);
DEFINE_PROPERTY(PROP_FRICTION, Friction, friction, float, ENTITY_ITEM_DEFAULT_FRICTION);
DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float, ENTITY_ITEM_DEFAULT_LIFETIME);
DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64, UNKNOWN_CREATED_TIME);
DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString, ENTITY_ITEM_DEFAULT_SCRIPT);
DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP);
DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL);
DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR);
DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color, particle::DEFAULT_COLOR_SPREAD);
DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED);
DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED);
DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA);
DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::DEFAULT_ALPHA_SPREAD);
DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::DEFAULT_ALPHA_START);
DEFINE_PROPERTY(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, particle::DEFAULT_ALPHA_FINISH);
DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString, "");
DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, "");
DEFINE_PROPERTY_REF(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT);
DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY);
DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING);
DEFINE_PROPERTY(PROP_COLLISIONLESS, Collisionless, collisionless, bool, ENTITY_ITEM_DEFAULT_COLLISIONLESS);
DEFINE_PROPERTY(PROP_COLLISION_MASK, CollisionMask, collisionMask, uint16_t, ENTITY_COLLISION_MASK_DEFAULT);
DEFINE_PROPERTY(PROP_DYNAMIC, Dynamic, dynamic, bool, ENTITY_ITEM_DEFAULT_DYNAMIC);
DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT);
DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY);
DEFINE_PROPERTY(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float, LightEntityItem::DEFAULT_FALLOFF_RADIUS);
DEFINE_PROPERTY(PROP_EXPONENT, Exponent, exponent, float, LightEntityItem::DEFAULT_EXPONENT);
DEFINE_PROPERTY(PROP_CUTOFF, Cutoff, cutoff, float, LightEntityItem::DEFAULT_CUTOFF);
DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool, ENTITY_ITEM_DEFAULT_LOCKED);
DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, "");
DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString, ENTITY_ITEM_DEFAULT_USER_DATA);
DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner, SimulationOwner());
DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString, TextEntityItem::DEFAULT_TEXT);
DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float, TextEntityItem::DEFAULT_LINE_HEIGHT);
DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR);
DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR);
DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL);
DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray, QByteArray());
// Cloning
DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE);
DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME);
DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT);
DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC);
DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY);
DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID);
// Scripts
DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString, ENTITY_ITEM_DEFAULT_SCRIPT);
DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP);
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
// Particles
DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType, SHAPE_TYPE_NONE);
DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::DEFAULT_MAX_PARTICLES);
DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float, particle::DEFAULT_LIFESPAN);
@ -184,45 +192,81 @@ public:
DEFINE_PROPERTY_REF(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat, particle::DEFAULT_EMIT_ORIENTATION);
DEFINE_PROPERTY_REF(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3, particle::DEFAULT_EMIT_DIMENSIONS);
DEFINE_PROPERTY(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float, particle::DEFAULT_EMIT_RADIUS_START);
DEFINE_PROPERTY_REF(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3, particle::DEFAULT_EMIT_ACCELERATION);
DEFINE_PROPERTY_REF(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3, particle::DEFAULT_ACCELERATION_SPREAD);
DEFINE_PROPERTY(PROP_POLAR_START, PolarStart, polarStart, float, particle::DEFAULT_POLAR_START);
DEFINE_PROPERTY(PROP_POLAR_FINISH, PolarFinish, polarFinish, float, particle::DEFAULT_POLAR_FINISH);
DEFINE_PROPERTY(PROP_AZIMUTH_START, AzimuthStart, azimuthStart, float, particle::DEFAULT_AZIMUTH_START);
DEFINE_PROPERTY(PROP_AZIMUTH_FINISH, AzimuthFinish, azimuthFinish, float, particle::DEFAULT_AZIMUTH_FINISH);
DEFINE_PROPERTY_REF(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3, particle::DEFAULT_EMIT_ACCELERATION);
DEFINE_PROPERTY_REF(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3, particle::DEFAULT_ACCELERATION_SPREAD);
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float, particle::DEFAULT_PARTICLE_RADIUS);
DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, particle::DEFAULT_RADIUS_SPREAD);
DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, particle::DEFAULT_RADIUS_START);
DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::DEFAULT_RADIUS_FINISH);
DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR);
DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color, particle::DEFAULT_COLOR_SPREAD);
DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED);
DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED);
DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA);
DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::DEFAULT_ALPHA_SPREAD);
DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::DEFAULT_ALPHA_START);
DEFINE_PROPERTY(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float, particle::DEFAULT_ALPHA_FINISH);
DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, "");
DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, particle::DEFAULT_EMITTER_SHOULD_TRAIL);
DEFINE_PROPERTY(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::DEFAULT_PARTICLE_SPIN);
DEFINE_PROPERTY(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::DEFAULT_SPIN_SPREAD);
DEFINE_PROPERTY(PROP_SPIN_START, SpinStart, spinStart, float, particle::DEFAULT_SPIN_START);
DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH);
DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY);
// Model
DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString, "");
DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, "");
DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector<bool>, QVector<bool>());
DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector<glm::quat>, QVector<glm::quat>());
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>, QVector<bool>());
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup);
// Light
DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool, LightEntityItem::DEFAULT_IS_SPOTLIGHT);
DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float, LightEntityItem::DEFAULT_INTENSITY);
DEFINE_PROPERTY(PROP_EXPONENT, Exponent, exponent, float, LightEntityItem::DEFAULT_EXPONENT);
DEFINE_PROPERTY(PROP_CUTOFF, Cutoff, cutoff, float, LightEntityItem::DEFAULT_CUTOFF);
DEFINE_PROPERTY(PROP_FALLOFF_RADIUS, FalloffRadius, falloffRadius, float, LightEntityItem::DEFAULT_FALLOFF_RADIUS);
// Text
DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString, TextEntityItem::DEFAULT_TEXT);
DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float, TextEntityItem::DEFAULT_LINE_HEIGHT);
DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR);
DEFINE_PROPERTY_REF(PROP_TEXT_ALPHA, TextAlpha, textAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA);
DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR);
DEFINE_PROPERTY_REF(PROP_BACKGROUND_ALPHA, BackgroundAlpha, backgroundAlpha, float, TextEntityItem::DEFAULT_TEXT_ALPHA);
DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE);
DEFINE_PROPERTY_REF(PROP_LEFT_MARGIN, LeftMargin, leftMargin, float, TextEntityItem::DEFAULT_MARGIN);
DEFINE_PROPERTY_REF(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float, TextEntityItem::DEFAULT_MARGIN);
DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN);
DEFINE_PROPERTY_REF(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float, TextEntityItem::DEFAULT_MARGIN);
// Zone
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);
DEFINE_PROPERTY_GROUP(AmbientLight, ambientLight, AmbientLightPropertyGroup);
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);
DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA);
DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE);
DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME);
DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup);
DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup);
DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup);
DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED);
DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED);
DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL);
DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup);
DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup);
DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup);
DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup);
DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, "");
DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH);
DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, "");
DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, "");
DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE);
DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray, QByteArray());
DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector<float>, QVector<float>());
DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true);
// Polyvox
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);
DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA);
DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE);
DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString, "");
DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString, "");
DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString, "");
@ -232,11 +276,25 @@ public:
DEFINE_PROPERTY_REF(PROP_X_P_NEIGHBOR_ID, XPNeighborID, xPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
// Web
DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, "");
DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI);
// Line
DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH);
DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
// Polyline
DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector<float>, QVector<float>());
DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true);
// Shape
DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere");
// Material
DEFINE_PROPERTY_REF(PROP_MATERIAL_URL, MaterialURL, materialURL, QString, "");
DEFINE_PROPERTY_REF_ENUM(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, materialMappingMode, MaterialMappingMode, UV);
DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, quint16, 0);
@ -247,19 +305,13 @@ public:
DEFINE_PROPERTY_REF(PROP_MATERIAL_DATA, MaterialData, materialData, QString, "");
DEFINE_PROPERTY_REF(PROP_MATERIAL_REPEAT, MaterialRepeat, materialRepeat, bool, true);
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
DEFINE_PROPERTY(PROP_PARTICLE_SPIN, ParticleSpin, particleSpin, float, particle::DEFAULT_PARTICLE_SPIN);
DEFINE_PROPERTY(PROP_SPIN_SPREAD, SpinSpread, spinSpread, float, particle::DEFAULT_SPIN_SPREAD);
DEFINE_PROPERTY(PROP_SPIN_START, SpinStart, spinStart, float, particle::DEFAULT_SPIN_START);
DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH);
DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY);
// Image
DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, "");
DEFINE_PROPERTY_REF(PROP_EMISSIVE, Emissive, emissive, bool, false);
DEFINE_PROPERTY_REF(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool, true);
DEFINE_PROPERTY_REF(PROP_SUB_IMAGE, SubImage, subImage, QRect, QRect());
// Grid
DEFINE_PROPERTY_REF(PROP_GRID_FOLLOW_CAMERA, FollowCamera, followCamera, bool, true);
DEFINE_PROPERTY(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t, GridEntityItem::DEFAULT_MAJOR_GRID_EVERY);
DEFINE_PROPERTY(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float, GridEntityItem::DEFAULT_MINOR_GRID_EVERY);
@ -284,34 +336,6 @@ public:
DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3);
DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3, ENTITY_ITEM_ZERO_VEC3);
DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector<bool>, QVector<bool>());
DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector<glm::quat>, QVector<glm::quat>());
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>, QVector<bool>());
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC);
DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED);
DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED);
DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL);
DEFINE_PROPERTY_REF_ENUM(PROP_ENTITY_HOST_TYPE, EntityHostType, entityHostType, entity::HostType, entity::HostType::DOMAIN);
DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI);
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_DEFAULT_CLONEABLE);
DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_DEFAULT_CLONE_LIFETIME);
DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_DEFAULT_CLONE_LIMIT);
DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC);
DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY);
DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID);
DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup);
static QString getComponentModeAsString(uint32_t mode);
std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT>::const_iterator findComponent(const QString& mode);
@ -361,9 +385,12 @@ public:
void setQueryAACubeDirty() { _queryAACubeChanged = true; }
void setLocationDirty() { _positionChanged = true; _rotationChanged = true; }
void setCreated(QDateTime& v);
bool hasTransformOrVelocityChanges() const;
void clearTransformOrVelocityChanges();
bool hasMiscPhysicsChanges() const;
bool hasSimulationRestrictedChanges() const;

View file

@ -190,6 +190,7 @@ QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) {
result = f.getHasProperty(PROP_GRAB_FOLLOWS_CONTROLLER) ? result + "grab.FollowsController " : result;
result = f.getHasProperty(PROP_GRAB_TRIGGERABLE) ? result + "grab.Triggerable " : result;
result = f.getHasProperty(PROP_GRAB_EQUIPPABLE) ? result + "grab.Equippable " : result;
result = f.getHasProperty(PROP_GRAB_DELEGATE_TO_PARENT) ? result + "grab.GrabDelegateToParent " : result;
result =
f.getHasProperty(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET) ? result + "grab.LeftEquippablePositionOffset " : result;
result =

View file

@ -267,6 +267,7 @@ enum EntityPropertyList {
PROP_GRAB_FOLLOWS_CONTROLLER,
PROP_GRAB_TRIGGERABLE,
PROP_GRAB_EQUIPPABLE,
PROP_GRAB_DELEGATE_TO_PARENT,
PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET,
PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET,
PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET,
@ -280,6 +281,11 @@ enum EntityPropertyList {
PROP_EMISSIVE,
PROP_SUB_IMAGE,
PROP_LEFT_MARGIN,
PROP_RIGHT_MARGIN,
PROP_TOP_MARGIN,
PROP_BOTTOM_MARGIN,
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties to end of list just ABOVE this line
PROP_AFTER_LAST_ITEM,
@ -291,10 +297,11 @@ enum EntityPropertyList {
// These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter
// since the derived class knows how to interpret it's own properties and knows the types it expects
PROP_TEXT_COLOR = PROP_COLOR,
PROP_TEXT_ALPHA = PROP_ALPHA,
PROP_TEXT = PROP_MODEL_URL,
PROP_LINE_HEIGHT = PROP_ANIMATION_URL,
PROP_BACKGROUND_COLOR = PROP_ANIMATION_FPS,
PROP_COLLISION_MODEL_URL_OLD_VERSION = PROP_ANIMATION_FPS + 1,
PROP_BACKGROUND_ALPHA = PROP_ALPHA_START,
// Aliases/Piggyback properties for Zones. These properties intentionally reuse the enum values for
// other properties which will never overlap with each other. We do this so that we don't have to expand

View file

@ -816,6 +816,10 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
QString previousUserdata;
if (entity) {
if (properties.hasTransformOrVelocityChanges() && entity->hasGrabs()) {
// if an entity is grabbed, the grab will override any position changes
properties.clearTransformOrVelocityChanges();
}
if (properties.hasSimulationRestrictedChanges()) {
if (_bidOnSimulationOwnership) {
// flag for simulation ownership, or upgrade existing ownership priority
@ -2268,3 +2272,126 @@ bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& en
}
return result;
}
glm::vec3 EntityScriptingInterface::worldToLocalPosition(glm::vec3 worldPosition, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 localPosition = SpatiallyNestable::worldToLocal(worldPosition, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return localPosition;
} else {
return glm::vec3(0.0f);
}
}
glm::quat EntityScriptingInterface::worldToLocalRotation(glm::quat worldRotation, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::quat localRotation = SpatiallyNestable::worldToLocal(worldRotation, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return localRotation;
} else {
return glm::quat();
}
}
glm::vec3 EntityScriptingInterface::worldToLocalVelocity(glm::vec3 worldVelocity, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 localVelocity = SpatiallyNestable::worldToLocalVelocity(worldVelocity, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return localVelocity;
} else {
return glm::vec3(0.0f);
}
}
glm::vec3 EntityScriptingInterface::worldToLocalAngularVelocity(glm::vec3 worldAngularVelocity, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 localAngularVelocity = SpatiallyNestable::worldToLocalAngularVelocity(worldAngularVelocity, parentID,
parentJointIndex, scalesWithParent,
success);
if (success) {
return localAngularVelocity;
} else {
return glm::vec3(0.0f);
}
}
glm::vec3 EntityScriptingInterface::worldToLocalDimensions(glm::vec3 worldDimensions, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 localDimensions = SpatiallyNestable::worldToLocalDimensions(worldDimensions, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return localDimensions;
} else {
return glm::vec3(0.0f);
}
}
glm::vec3 EntityScriptingInterface::localToWorldPosition(glm::vec3 localPosition, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 worldPosition = SpatiallyNestable::localToWorld(localPosition, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return worldPosition;
} else {
return glm::vec3(0.0f);
}
}
glm::quat EntityScriptingInterface::localToWorldRotation(glm::quat localRotation, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::quat worldRotation = SpatiallyNestable::localToWorld(localRotation, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return worldRotation;
} else {
return glm::quat();
}
}
glm::vec3 EntityScriptingInterface::localToWorldVelocity(glm::vec3 localVelocity, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 worldVelocity = SpatiallyNestable::localToWorldVelocity(localVelocity, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return worldVelocity;
} else {
return glm::vec3(0.0f);
}
}
glm::vec3 EntityScriptingInterface::localToWorldAngularVelocity(glm::vec3 localAngularVelocity, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 worldAngularVelocity = SpatiallyNestable::localToWorldAngularVelocity(localAngularVelocity,
parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return worldAngularVelocity;
} else {
return glm::vec3(0.0f);
}
}
glm::vec3 EntityScriptingInterface::localToWorldDimensions(glm::vec3 localDimensions, const QUuid& parentID,
int parentJointIndex, bool scalesWithParent) {
bool success;
glm::vec3 worldDimensions = SpatiallyNestable::localToWorldDimensions(localDimensions, parentID, parentJointIndex,
scalesWithParent, success);
if (success) {
return worldDimensions;
} else {
return glm::vec3(0.0f);
}
}

View file

@ -1573,6 +1573,109 @@ public slots:
* print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
/**jsdoc
* @function Entities.worldToLocalPosition
* @param {Vec3} worldPosition
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 worldToLocalPosition(glm::vec3 worldPosition, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.worldToLocalRotation
* @param {Quat} worldRotation
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Quat}
*/
Q_INVOKABLE glm::quat worldToLocalRotation(glm::quat worldRotation, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.worldToLocalVelocity
* @param {Vec3} worldVelocity
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 worldToLocalVelocity(glm::vec3 worldVelocity, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.worldToLocalAngularVelocity
* @param {Vec3} worldAngularVelocity
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 worldToLocalAngularVelocity(glm::vec3 worldAngularVelocity, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.worldToLocalDimensions
* @param {Vec3} worldDimensions
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 worldToLocalDimensions(glm::vec3 worldDimensions, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.localToWorldPosition
* @param {Vec3} localPosition
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 localToWorldPosition(glm::vec3 localPosition, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.localToWorldRotation
* @param {Quat} localRotation
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Quat}
*/
Q_INVOKABLE glm::quat localToWorldRotation(glm::quat localRotation, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.localToWorldVelocity
* @param {Vec3} localVelocity
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 localToWorldVelocity(glm::vec3 localVelocity, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.localToWorldAngularVelocity
* @param {Vec3} localAngularVelocity
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 localToWorldAngularVelocity(glm::vec3 localAngularVelocity, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* @function Entities.localToWorldDimensions
* @param {Vec3} localDimensions
* @param {Uuid} parentID
* @param {number} parentJointIndex
* @param {boolean} scalesWithparent
* @returns {Vec3}
*/
Q_INVOKABLE glm::vec3 localToWorldDimensions(glm::vec3 localDimensions, const QUuid& parentID,
int parentJointIndex = -1, bool scalesWithParent = false);
/**jsdoc
* Get the static certificate for an entity. The static certificate contains static properties of the item which cannot
* be altered.

View file

@ -2586,6 +2586,8 @@ void convertGrabUserDataToProperties(EntityItemProperties& properties) {
grabProperties.setEquippable(equippable.toBool());
}
grabProperties.setGrabDelegateToParent(true);
if (grabbableKey["spatialKey"].isObject()) {
QJsonObject spatialKey = grabbableKey["spatialKey"].toObject();
grabProperties.setEquippable(true);
@ -2956,3 +2958,50 @@ bool EntityTree::removeMaterialFromOverlay(const QUuid& overlayID, graphics::Mat
}
return false;
}
void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
MovingEntitiesOperator& moveOperator, bool force, bool tellServer) {
// if the queryBox has changed, tell the entity-server
EntityItemPointer entity = std::dynamic_pointer_cast<EntityItem>(object);
if (entity && (entity->updateQueryAACube() || force)) {
bool success;
AACube newCube = entity->getQueryAACube(success);
if (success) {
moveOperator.addEntityToMoveList(entity, newCube);
}
// send an edit packet to update the entity-server about the queryAABox. We do this for domain-hosted
// entities as well as for avatar-entities; the packet-sender will route the update accordingly
if (tellServer && packetSender && (entity->isDomainEntity() || entity->isAvatarEntity())) {
quint64 now = usecTimestampNow();
EntityItemProperties properties = entity->getProperties();
properties.setQueryAACubeDirty();
properties.setLocationDirty();
properties.setLastEdited(now);
packetSender->queueEditEntityMessage(PacketType::EntityEdit, getThisPointer(), entity->getID(), properties);
entity->setLastBroadcast(now); // for debug/physics status icons
}
entity->markDirtyFlags(Simulation::DIRTY_POSITION);
entityChanged(entity);
}
object->forEachDescendant([&](SpatiallyNestablePointer descendant) {
updateEntityQueryAACubeWorker(descendant, packetSender, moveOperator, force, tellServer);
});
}
void EntityTree::updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
bool force, bool tellServer) {
// This is used when something other than a script or physics moves an entity. We need to put it in the
// correct place in our local octree, update its and its children's queryAACubes, and send an edit
// packet to the entity-server.
MovingEntitiesOperator moveOperator;
updateEntityQueryAACubeWorker(object, packetSender, moveOperator, force, tellServer);
if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator");
recurseTreeWithOperator(&moveOperator);
}
}

View file

@ -277,6 +277,9 @@ public:
std::map<QString, QString> getNamedPaths() const { return _namedPaths; }
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
bool force, bool tellServer);
signals:
void deletingEntity(const EntityItemID& entityID);
void deletingEntityPointer(EntityItem* entityID);
@ -392,6 +395,9 @@ private:
bool _serverlessDomain { false };
std::map<QString, QString> _namedPaths;
void updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
MovingEntitiesOperator& moveOperator, bool force, bool tellServer);
};
void convertGrabUserDataToProperties(EntityItemProperties& properties);

View file

@ -24,6 +24,8 @@ void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProp
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_DELEGATE_TO_PARENT, Grab, grab,
GrabDelegateToParent, grabDelegateToParent);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab,
EquippableLeftPosition, equippableLeftPosition);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab,
@ -141,6 +143,7 @@ bool GrabPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController());
APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable());
APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable());
APPEND_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, getGrabDelegateToParent());
APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition());
APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation());
APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition());
@ -164,6 +167,7 @@ bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags,
READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController);
READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable);
READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable);
READ_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, bool, setGrabDelegateToParent);
READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition);
READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation);
READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition);
@ -177,6 +181,7 @@ bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags,
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_TRIGGERABLE, Triggerable);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE, Equippable);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_DELEGATE_TO_PARENT, GrabDelegateToParent);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition);
@ -215,6 +220,7 @@ EntityPropertyFlags GrabPropertyGroup::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_GRAB_FOLLOWS_CONTROLLER, grabFollowsController);
CHECK_PROPERTY_CHANGE(PROP_GRAB_TRIGGERABLE, triggerable);
CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE, equippable);
CHECK_PROPERTY_CHANGE(PROP_GRAB_DELEGATE_TO_PARENT, grabDelegateToParent);
CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, equippableLeftPosition);
CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, equippableLeftRotation);
CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, equippableRightPosition);
@ -273,6 +279,7 @@ EntityPropertyFlags GrabPropertyGroup::getEntityProperties(EncodeBitstreamParams
requestedProperties += PROP_GRAB_FOLLOWS_CONTROLLER;
requestedProperties += PROP_GRAB_TRIGGERABLE;
requestedProperties += PROP_GRAB_EQUIPPABLE;
requestedProperties += PROP_GRAB_DELEGATE_TO_PARENT;
requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET;
requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET;
requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET;
@ -299,6 +306,7 @@ void GrabPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeB
APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController());
APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable());
APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable());
APPEND_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, getGrabDelegateToParent());
APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition());
APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation());
APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition());
@ -321,6 +329,7 @@ int GrabPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* dat
READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController);
READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable);
READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable);
READ_ENTITY_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, bool, setGrabDelegateToParent);
READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition);
READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation);
READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition);

View file

@ -31,6 +31,7 @@ static const bool INITIAL_KINEMATIC { true };
static const bool INITIAL_FOLLOWS_CONTROLLER { true };
static const bool INITIAL_TRIGGERABLE { false };
static const bool INITIAL_EQUIPPABLE { false };
static const bool INITIAL_GRAB_DELEGATE_TO_PARENT { true };
static const glm::vec3 INITIAL_LEFT_EQUIPPABLE_POSITION { glm::vec3(0.0f) };
static const glm::quat INITIAL_LEFT_EQUIPPABLE_ROTATION { glm::quat() };
static const glm::vec3 INITIAL_RIGHT_EQUIPPABLE_POSITION { glm::vec3(0.0f) };
@ -123,6 +124,8 @@ public:
INITIAL_FOLLOWS_CONTROLLER);
DEFINE_PROPERTY(PROP_GRAB_TRIGGERABLE, Triggerable, triggerable, bool, INITIAL_TRIGGERABLE);
DEFINE_PROPERTY(PROP_GRAB_EQUIPPABLE, Equippable, equippable, bool, INITIAL_EQUIPPABLE);
DEFINE_PROPERTY(PROP_GRAB_DELEGATE_TO_PARENT, GrabDelegateToParent, grabDelegateToParent, bool,
INITIAL_GRAB_DELEGATE_TO_PARENT);
DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition, equippableLeftPosition,
glm::vec3, INITIAL_LEFT_EQUIPPABLE_POSITION);
DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation, equippableLeftRotation,

View file

@ -26,7 +26,9 @@
const QString TextEntityItem::DEFAULT_TEXT("");
const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f;
const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 };
const float TextEntityItem::DEFAULT_TEXT_ALPHA = 1.0f;
const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0};
const float TextEntityItem::DEFAULT_MARGIN = 0.0f;
EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer entity(new TextEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
@ -50,8 +52,14 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de
COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textAlpha, getTextAlpha);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundAlpha, getBackgroundAlpha);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(leftMargin, getLeftMargin);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rightMargin, getRightMargin);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(topMargin, getTopMargin);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(bottomMargin, getBottomMargin);
return properties;
}
@ -62,8 +70,14 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textAlpha, setTextAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundAlpha, setBackgroundAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(leftMargin, setLeftMargin);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rightMargin, setRightMargin);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(topMargin, setTopMargin);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bottomMargin, setBottomMargin);
if (somethingChanged) {
bool wantDebug = false;
@ -90,8 +104,14 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText);
READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight);
READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor);
READ_ENTITY_PROPERTY(PROP_TEXT_ALPHA, float, setTextAlpha);
READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, glm::u8vec3, setBackgroundColor);
READ_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, float, setBackgroundAlpha);
READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode);
READ_ENTITY_PROPERTY(PROP_LEFT_MARGIN, float, setLeftMargin);
READ_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, float, setRightMargin);
READ_ENTITY_PROPERTY(PROP_TOP_MARGIN, float, setTopMargin);
READ_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, float, setBottomMargin);
return bytesRead;
}
@ -101,8 +121,14 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p
requestedProperties += PROP_TEXT;
requestedProperties += PROP_LINE_HEIGHT;
requestedProperties += PROP_TEXT_COLOR;
requestedProperties += PROP_TEXT_ALPHA;
requestedProperties += PROP_BACKGROUND_COLOR;
requestedProperties += PROP_BACKGROUND_ALPHA;
requestedProperties += PROP_BILLBOARD_MODE;
requestedProperties += PROP_LEFT_MARGIN;
requestedProperties += PROP_RIGHT_MARGIN;
requestedProperties += PROP_TOP_MARGIN;
requestedProperties += PROP_BOTTOM_MARGIN;
return requestedProperties;
}
@ -119,8 +145,14 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
APPEND_ENTITY_PROPERTY(PROP_TEXT, getText());
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight());
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor());
APPEND_ENTITY_PROPERTY(PROP_TEXT_ALPHA, getTextAlpha());
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor());
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_ALPHA, getBackgroundAlpha());
APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode());
APPEND_ENTITY_PROPERTY(PROP_LEFT_MARGIN, getLeftMargin());
APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, getRightMargin());
APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, getTopMargin());
APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, getBottomMargin());
}
@ -216,6 +248,18 @@ glm::u8vec3 TextEntityItem::getTextColor() const {
});
}
void TextEntityItem::setTextAlpha(float value) {
withWriteLock([&] {
_textAlpha = value;
});
}
float TextEntityItem::getTextAlpha() const {
return resultWithReadLock<float>([&] {
return _textAlpha;
});
}
void TextEntityItem::setBackgroundColor(const glm::u8vec3& value) {
withWriteLock([&] {
_backgroundColor = value;
@ -228,6 +272,18 @@ glm::u8vec3 TextEntityItem::getBackgroundColor() const {
});
}
void TextEntityItem::setBackgroundAlpha(float value) {
withWriteLock([&] {
_backgroundAlpha = value;
});
}
float TextEntityItem::getBackgroundAlpha() const {
return resultWithReadLock<float>([&] {
return _backgroundAlpha;
});
}
BillboardMode TextEntityItem::getBillboardMode() const {
BillboardMode result;
withReadLock([&] {
@ -242,3 +298,50 @@ void TextEntityItem::setBillboardMode(BillboardMode value) {
});
}
void TextEntityItem::setLeftMargin(float value) {
withWriteLock([&] {
_leftMargin = value;
});
}
float TextEntityItem::getLeftMargin() const {
return resultWithReadLock<float>([&] {
return _leftMargin;
});
}
void TextEntityItem::setRightMargin(float value) {
withWriteLock([&] {
_rightMargin = value;
});
}
float TextEntityItem::getRightMargin() const {
return resultWithReadLock<float>([&] {
return _rightMargin;
});
}
void TextEntityItem::setTopMargin(float value) {
withWriteLock([&] {
_topMargin = value;
});
}
float TextEntityItem::getTopMargin() const {
return resultWithReadLock<float>([&] {
return _topMargin;
});
}
void TextEntityItem::setBottomMargin(float value) {
withWriteLock([&] {
_bottomMargin = value;
});
}
float TextEntityItem::getBottomMargin() const {
return resultWithReadLock<float>([&] {
return _bottomMargin;
});
}

View file

@ -67,19 +67,45 @@ public:
glm::u8vec3 getTextColor() const;
void setTextColor(const glm::u8vec3& value);
static const float DEFAULT_TEXT_ALPHA;
float getTextAlpha() const;
void setTextAlpha(float value);
static const glm::u8vec3 DEFAULT_BACKGROUND_COLOR;
glm::u8vec3 getBackgroundColor() const;
void setBackgroundColor(const glm::u8vec3& value);
float getBackgroundAlpha() const;
void setBackgroundAlpha(float value);
BillboardMode getBillboardMode() const;
void setBillboardMode(BillboardMode value);
static const float DEFAULT_MARGIN;
float getLeftMargin() const;
void setLeftMargin(float value);
float getRightMargin() const;
void setRightMargin(float value);
float getTopMargin() const;
void setTopMargin(float value);
float getBottomMargin() const;
void setBottomMargin(float value);
private:
QString _text;
float _lineHeight;
glm::u8vec3 _textColor;
float _textAlpha;
glm::u8vec3 _backgroundColor;
float _backgroundAlpha;
BillboardMode _billboardMode;
float _leftMargin;
float _rightMargin;
float _topMargin;
float _bottomMargin;
};
#endif // hifi_TextEntityItem_h

View file

@ -300,10 +300,10 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
} else {
HIFI_FCDEBUG(networking(), "Replicated packet of type" << headerType
<< "received from unknown upstream" << packet.getSenderSockAddr());
return false;
}
} else {
emit dataReceived(NodeType::Unassigned, packet.getPayloadSize());
return true;
@ -319,7 +319,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
SharedNodePointer matchingNode = nodeWithLocalID(sourceLocalID);
sourceNode = matchingNode.data();
}
QUuid sourceID = sourceNode ? sourceNode->getUUID() : QUuid();
if (!sourceNode &&
@ -1261,6 +1261,10 @@ void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep) {
}
void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) {
if (!_flagTimeForConnectionStep) {
// this is only true in interface
return;
}
if (connectionStep == ConnectionStep::LookupAddress) {
QWriteLocker writeLock(&_connectionTimeLock);

View file

@ -122,7 +122,7 @@ public:
bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
@ -204,9 +204,9 @@ public:
// This allows multiple threads (i.e. a thread pool) to share a lock
// without deadlocking when a dying node attempts to acquire a write lock
template<typename NestedNodeLambda>
void nestedEach(NestedNodeLambda functor,
int* lockWaitOut = nullptr,
int* nodeTransformOut = nullptr,
void nestedEach(NestedNodeLambda functor,
int* lockWaitOut = nullptr,
int* nodeTransformOut = nullptr,
int* functorOut = nullptr) {
auto start = usecTimestampNow();
{
@ -310,6 +310,9 @@ public:
void setAuthenticatePackets(bool useAuthentication) { _useAuthentication = useAuthentication; }
bool getAuthenticatePackets() const { return _useAuthentication; }
void setFlagTimeForConnectionStep(bool flag) { _flagTimeForConnectionStep = flag; }
bool isFlagTimeForConnectionStep() { return _flagTimeForConnectionStep; }
static void makeSTUNRequestPacket(char* stunRequestPacket);
#if (PR_BUILD || DEV_BUILD)
@ -440,6 +443,7 @@ private:
using LocalIDMapping = tbb::concurrent_unordered_map<Node::LocalID, SharedNodePointer>;
LocalIDMapping _localIDMap;
Node::LocalID _sessionLocalID { 0 };
bool _flagTimeForConnectionStep { false }; // only keep track in interface
};
#endif // hifi_LimitedNodeList_h

View file

@ -33,14 +33,14 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit:
case PacketType::EntityData:
case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::GridEntities);
return static_cast<PacketVersion>(EntityVersion::GrabTraits);
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
case PacketType::AvatarIdentity:
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
case PacketType::KillAvatar:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::JointTransScaled);
return static_cast<PacketVersion>(AvatarMixerPacketVersion::GrabTraits);
case PacketType::MessagesData:
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
// ICE packets

View file

@ -249,7 +249,9 @@ enum class EntityVersion : PacketVersion {
EntityHostTypes,
CleanupProperties,
ImageEntities,
GridEntities
GridEntities,
MissingTextProperties,
GrabTraits
};
enum class EntityScriptCallMethodVersion : PacketVersion {
@ -303,6 +305,7 @@ enum class AvatarMixerPacketVersion : PacketVersion {
MigrateAvatarEntitiesToTraits,
FarGrabJointsRedux,
JointTransScaled,
GrabTraits,
AvatarTraitsAck
};

View file

@ -33,36 +33,53 @@ ObjectActionTractor::ObjectActionTractor(const QUuid& id, EntityItemPointer owne
_rotationalTargetSet(true),
_linearVelocityTarget(0.0f)
{
#if WANT_DEBUG
#if WANT_DEBUG
qCDebug(physics) << "ObjectActionTractor::ObjectActionTractor";
#endif
#endif
}
ObjectActionTractor::~ObjectActionTractor() {
#if WANT_DEBUG
#if WANT_DEBUG
qCDebug(physics) << "ObjectActionTractor::~ObjectActionTractor";
#endif
#endif
}
bool ObjectActionTractor::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
float& linearTimeScale, float& angularTimeScale) {
bool success { true };
EntityItemPointer other = std::dynamic_pointer_cast<EntityItem>(getOther());
withReadLock([&]{
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
float& linearTimeScale, float& angularTimeScale) {
SpatiallyNestablePointer other = getOther();
return resultWithReadLock<bool>([&]{
linearTimeScale = _linearTimeScale;
angularTimeScale = _angularTimeScale;
if (!_otherID.isNull()) {
if (other && other->isReadyToComputeShape()) {
rotation = _desiredRotationalTarget * other->getWorldOrientation();
position = other->getWorldOrientation() * _desiredPositionalTarget + other->getWorldPosition();
bool otherIsReady { true };
if (other && other->getNestableType() == NestableType::Entity) {
EntityItemPointer otherEntity = std::static_pointer_cast<EntityItem>(other);
otherIsReady = otherEntity->isReadyToComputeShape();
}
if (other && otherIsReady) {
bool success;
glm::vec3 otherWorldPosition = other->getWorldPosition(_otherJointIndex, success);
if (!success) {
linearTimeScale = FLT_MAX;
angularTimeScale = FLT_MAX;
return false;
}
glm::quat otherWorldOrientation = other->getWorldOrientation(_otherJointIndex, success);
if (!success) {
linearTimeScale = FLT_MAX;
angularTimeScale = FLT_MAX;
return false;
}
rotation = otherWorldOrientation * _desiredRotationalTarget;
position = otherWorldOrientation * _desiredPositionalTarget + otherWorldPosition;
} else {
// we should have an "other" but can't find it, or its collision shape isn't loaded,
// so disable the tractor.
linearTimeScale = FLT_MAX;
angularTimeScale = FLT_MAX;
success = false;
return false;
}
} else {
rotation = _desiredRotationalTarget;
@ -70,8 +87,8 @@ bool ObjectActionTractor::getTarget(float deltaTimeStep, glm::quat& rotation, gl
}
linearVelocity = glm::vec3();
angularVelocity = glm::vec3();
return true;
});
return success;
}
bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
@ -108,9 +125,9 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
float linearTimeScale;
float angularTimeScale;
bool success = tractorAction->getTarget(deltaTimeStep,
rotationForAction, positionForAction,
linearVelocityForAction, angularVelocityForAction,
linearTimeScale, angularTimeScale);
rotationForAction, positionForAction,
linearVelocityForAction, angularVelocityForAction,
linearTimeScale, angularTimeScale);
if (success) {
if (angularTimeScale < MAX_TRACTOR_TIMESCALE) {
angularTractorCount++;
@ -239,7 +256,7 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) {
glm::quat rotationalTarget;
float angularTimeScale;
QUuid otherID;
int otherJointIndex;
bool needUpdate = false;
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
@ -274,18 +291,25 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) {
}
ok = true;
otherID = QUuid(EntityDynamicInterface::extractStringArgument("tractor action",
arguments, "otherID", ok, false));
otherID = QUuid(EntityDynamicInterface::extractStringArgument("tractor action", arguments, "otherID", ok, false));
if (!ok) {
otherID = _otherID;
}
ok = true;
otherJointIndex = EntityDynamicInterface::extractIntegerArgument("tractor action", arguments,
"otherJointIndex", ok, false);
if (!ok) {
otherJointIndex = _otherJointIndex;
}
if (somethingChanged ||
positionalTarget != _desiredPositionalTarget ||
linearTimeScale != _linearTimeScale ||
rotationalTarget != _desiredRotationalTarget ||
angularTimeScale != _angularTimeScale ||
otherID != _otherID) {
otherID != _otherID ||
otherJointIndex != _otherJointIndex) {
// something changed
needUpdate = true;
}
@ -298,6 +322,7 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) {
_desiredRotationalTarget = rotationalTarget;
_angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
_otherID = otherID;
_otherJointIndex = otherJointIndex;
_active = true;
auto ownerEntity = _ownerEntity.lock();
@ -313,20 +338,22 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) {
}
/**jsdoc
* The <code>"tractor"</code> {@link Entities.ActionType|ActionType} moves and rotates an entity to a target position and
* The <code>"tractor"</code> {@link Entities.ActionType|ActionType} moves and rotates an entity to a target position and
* orientation, optionally relative to another entity.
* It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}.
*
* @typedef {object} Entities.ActionArguments-Tractor
* @property {Vec3} targetPosition=0,0,0 - The target position.
* @property {Quat} targetRotation=0,0,0,1 - The target rotation.
* @property {Uuid} otherID=null - If an entity ID, the <code>targetPosition</code> and <code>targetRotation</code> are
* @property {Uuid} otherID=null - If an entity ID, the <code>targetPosition</code> and <code>targetRotation</code> are
* relative to this entity's position and rotation.
* @property {Uuid} otherJointIndex=null - If an entity JointIndex, the <code>targetPosition</code> and
* <code>targetRotation</code> are relative to this entity's joint's position and rotation.
* @property {number} linearTimeScale=3.4e+38 - Controls how long it takes for the entity's position to catch up with the
* target position. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action
* target position. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action
* is applied using an exponential decay.
* @property {number} angularTimeScale=3.4e+38 - Controls how long it takes for the entity's orientation to catch up with the
* target orientation. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the
* target orientation. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the
* action is applied using an exponential decay.
*/
QVariantMap ObjectActionTractor::getArguments() {
@ -339,6 +366,7 @@ QVariantMap ObjectActionTractor::getArguments() {
arguments["angularTimeScale"] = _angularTimeScale;
arguments["otherID"] = _otherID;
arguments["otherJointIndex"] = _otherJointIndex;
});
return arguments;
}
@ -354,6 +382,7 @@ void ObjectActionTractor::serializeParameters(QDataStream& dataStream) const {
dataStream << localTimeToServerTime(_expires);
dataStream << _tag;
dataStream << _otherID;
dataStream << _otherJointIndex;
});
}
@ -387,6 +416,7 @@ void ObjectActionTractor::deserializeParameters(QByteArray serializedArguments,
dataStream >> _tag;
dataStream >> _otherID;
dataStream >> _otherJointIndex;
_active = true;
});

View file

@ -66,6 +66,7 @@ protected:
EntityItemID _otherID;
SpatiallyNestableWeakPointer _other;
SpatiallyNestablePointer getOther();
int _otherJointIndex { -1 };
private:
qint64 getEntityServerClockSkew() const;

View file

@ -67,11 +67,11 @@ float TextRenderer3D::getFontSize() const {
}
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
const glm::vec2& bounds, bool layered) {
const glm::vec2& bounds) {
// The font does all the OpenGL work
if (_font) {
_color = color;
_font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, layered);
_font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds);
}
}

View file

@ -39,7 +39,7 @@ public:
float getFontSize() const; // Pixel size
void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(1.0f),
const glm::vec2& bounds = glm::vec2(-1.0f), bool layered = false);
const glm::vec2& bounds = glm::vec2(-1.0f));
private:
TextRenderer3D(const char* family, float pointSize, int weight = -1, bool italic = false,

View file

@ -222,7 +222,6 @@ void Font::setupGPU() {
// Setup render pipeline
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D);
gpu::ShaderPointer programTransparent = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D_transparent);
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
@ -232,13 +231,14 @@ void Font::setupGPU() {
PrepareStencil::testMaskDrawShape(*state);
_pipeline = gpu::Pipeline::create(program, state);
gpu::ShaderPointer programTransparent = gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D_transparent);
auto transparentState = std::make_shared<gpu::State>();
transparentState->setCullMode(gpu::State::CULL_BACK);
transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
transparentState->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMaskDrawShape(*transparentState);
PrepareStencil::testMask(*transparentState);
_transparentPipeline = gpu::Pipeline::create(programTransparent, transparentState);
}
@ -289,7 +289,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
break;
}
}
if ((bounds.y != -1) && (advance.y - _fontSize < -origin.y - bounds.y)) {
if ((bounds.y != -1) && (advance.y - _fontSize < origin.y - bounds.y)) {
// We are out of the y bound, stop drawing
break;
}
@ -343,7 +343,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
}
void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool layered) {
EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds) {
if (str == "") {
return;
}
@ -370,7 +370,7 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
}
// need the gamma corrected color here
batch.setPipeline((color.a < 1.0f || layered) ? _transparentPipeline : _pipeline);
batch.setPipeline((color.a < 1.0f) ? _transparentPipeline : _pipeline);
batch.setInputFormat(_format);
batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride);
batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture);

View file

@ -46,7 +46,7 @@ public:
// Render string to batch
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str,
const glm::vec4& color, EffectType effectType,
const glm::vec2& origin, const glm::vec2& bound, bool layered = false);
const glm::vec2& origin, const glm::vec2& bound);
static Pointer load(const QString& family);

View file

@ -0,0 +1,70 @@
//
// Grab.cpp
// libraries/avatars/src
//
// Created by Seth Alves on 2018-9-1.
// 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 "Grab.h"
QByteArray Grab::toByteArray() {
QByteArray ba;
QDataStream dataStream(&ba, QIODevice::WriteOnly);
const int dataEncodingVersion = 1;
dataStream << dataEncodingVersion << _ownerID << _targetID << _parentJointIndex
<< _hand << _positionalOffset << _rotationalOffset;
return ba;
}
bool Grab::fromByteArray(const QByteArray& grabData) {
QDataStream dataStream(grabData);
int dataEncodingVersion;
QUuid newOwnerID { QUuid() };
QUuid newTargetID { QUuid() };
int newParentJointIndex { -1 };
QString newHand { "none" };
glm::vec3 newPositionalOffset { glm::vec3(0.0f) };
glm::quat newRotationalOffset { glm::quat() };
dataStream >> dataEncodingVersion;
assert(dataEncodingVersion == 1);
dataStream >> newOwnerID;
dataStream >> newTargetID;
dataStream >> newParentJointIndex;
dataStream >> newHand;
dataStream >> newPositionalOffset;
dataStream >> newRotationalOffset;
bool somethingChanged { false };
if (_ownerID != newOwnerID) {
_ownerID = newOwnerID;
somethingChanged = true;
}
if (_targetID != newTargetID) {
_targetID = newTargetID;
somethingChanged = true;
}
if (_parentJointIndex != newParentJointIndex) {
_parentJointIndex = newParentJointIndex;
somethingChanged = true;
}
if (_hand != newHand) {
_hand = newHand;
somethingChanged = true;
}
if (_positionalOffset != newPositionalOffset) {
_positionalOffset = newPositionalOffset;
somethingChanged = true;
}
if (_rotationalOffset != newRotationalOffset) {
_rotationalOffset = newRotationalOffset;
somethingChanged = true;
}
return somethingChanged;
}

View file

@ -0,0 +1,99 @@
//
// Grab.h
// libraries/avatars/src
//
// Created by Seth Alves on 2018-9-1.
// 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_Grab_h
#define hifi_Grab_h
#include <QUuid>
#include <QByteArray>
#include "GLMHelpers.h"
#include "StreamUtils.h"
class Grab;
using GrabPointer = std::shared_ptr<Grab>;
using GrabWeakPointer = std::weak_ptr<Grab>;
class GrabLocationAccumulator {
public:
void accumulate(glm::vec3 position, glm::quat orientation) {
_position += position;
_orientation = orientation; // XXX
count++;
}
glm::vec3 finalizePosition() { return count > 0 ? _position * (1.0f / count) : glm::vec3(0.0f); }
glm::quat finalizeOrientation() { return _orientation; } // XXX
protected:
glm::vec3 _position;
glm::quat _orientation;
int count { 0 };
};
class Grab {
public:
Grab() {};
Grab(const QUuid& newOwnerID, const QUuid& newTargetID, int newParentJointIndex, const QString& newHand,
glm::vec3 newPositionalOffset, glm::quat newRotationalOffset) :
_ownerID(newOwnerID),
_targetID(newTargetID),
_parentJointIndex(newParentJointIndex),
_hand(newHand),
_positionalOffset(newPositionalOffset),
_rotationalOffset(newRotationalOffset) {}
QByteArray toByteArray();
bool fromByteArray(const QByteArray& grabData);
Grab& operator=(const GrabPointer& other) {
_ownerID = other->_ownerID;
_targetID = other->_targetID;
_parentJointIndex = other->_parentJointIndex;
_hand = other->_hand;
_positionalOffset = other->_positionalOffset;
_rotationalOffset = other->_rotationalOffset;
_actionID = other->_actionID;
return *this;
}
QUuid getActionID() const { return _actionID; }
void setActionID(const QUuid& actionID) { _actionID = actionID; }
QUuid getOwnerID() const { return _ownerID; }
void setOwnerID(QUuid ownerID) { _ownerID = ownerID; }
QUuid getTargetID() const { return _targetID; }
void setTargetID(QUuid targetID) { _targetID = targetID; }
int getParentJointIndex() const { return _parentJointIndex; }
void setParentJointIndex(int parentJointIndex) { _parentJointIndex = parentJointIndex; }
QString getHand() const { return _hand; }
void setHand(QString hand) { _hand = hand; }
glm::vec3 getPositionalOffset() const { return _positionalOffset; }
void setPositionalOffset(glm::vec3 positionalOffset) { _positionalOffset = positionalOffset; }
glm::quat getRotationalOffset() const { return _rotationalOffset; }
void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; }
protected:
QUuid _actionID; // if an action is created in bullet for this grab, this is the ID
QUuid _ownerID; // avatar ID of grabber
QUuid _targetID; // SpatiallyNestable ID of grabbed
int _parentJointIndex { -1 }; // which avatar joint is being used to grab
QString _hand; // "left" or "right"
glm::vec3 _positionalOffset; // relative to joint
glm::quat _rotationalOffset; // relative to joint
};
#endif // hifi_Grab_h

View file

@ -481,27 +481,29 @@ void SpatiallyNestable::setWorldTransform(const glm::vec3& position, const glm::
return;
}
bool changed = false;
bool success = true;
Transform parentTransform = getParentTransform(success);
_transformLock.withWriteLock([&] {
Transform myWorldTransform;
Transform::mult(myWorldTransform, parentTransform, _transform);
if (myWorldTransform.getRotation() != orientation) {
changed = true;
myWorldTransform.setRotation(orientation);
}
if (myWorldTransform.getTranslation() != position) {
changed = true;
myWorldTransform.setTranslation(position);
}
if (success) {
bool changed = false;
_transformLock.withWriteLock([&] {
Transform myWorldTransform;
Transform::mult(myWorldTransform, parentTransform, _transform);
if (myWorldTransform.getRotation() != orientation) {
changed = true;
myWorldTransform.setRotation(orientation);
}
if (myWorldTransform.getTranslation() != position) {
changed = true;
myWorldTransform.setTranslation(position);
}
if (changed) {
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
_translationChanged = usecTimestampNow();
}
});
if (changed) {
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
_translationChanged = usecTimestampNow();
locationChanged(false);
}
});
if (success && changed) {
locationChanged(false);
}
}
@ -788,19 +790,21 @@ void SpatiallyNestable::setTransform(const Transform& transform, bool& success)
return;
}
bool changed = false;
Transform parentTransform = getParentTransform(success);
_transformLock.withWriteLock([&] {
Transform beforeTransform = _transform;
Transform::inverseMult(_transform, parentTransform, transform);
if (_transform != beforeTransform) {
changed = true;
_translationChanged = usecTimestampNow();
_rotationChanged = usecTimestampNow();
if (success) {
bool changed = false;
_transformLock.withWriteLock([&] {
Transform beforeTransform = _transform;
Transform::inverseMult(_transform, parentTransform, transform);
if (_transform != beforeTransform) {
changed = true;
_translationChanged = usecTimestampNow();
_rotationChanged = usecTimestampNow();
}
});
if (changed) {
locationChanged();
}
});
if (success && changed) {
locationChanged();
}
}
@ -1369,3 +1373,43 @@ bool SpatiallyNestable::isParentPathComplete(int depth) const {
return parent->isParentPathComplete(depth + 1);
}
void SpatiallyNestable::addGrab(GrabPointer grab) {
_grabsLock.withWriteLock([&] {
_grabs.insert(grab);
});
}
void SpatiallyNestable::removeGrab(GrabPointer grab) {
_grabsLock.withWriteLock([&] {
_grabs.remove(grab);
});
}
bool SpatiallyNestable::hasGrabs() {
bool result { false };
_grabsLock.withReadLock([&] {
result = !_grabs.isEmpty();
});
return result;
}
QUuid SpatiallyNestable::getEditSenderID() {
// if more than one avatar is grabbing something, decide which one should tell the enity-server about it
QUuid editSenderID;
bool editSenderIDSet { false };
_grabsLock.withReadLock([&] {
foreach (const GrabPointer &grab, _grabs) {
QUuid ownerID = grab->getOwnerID();
if (!editSenderIDSet) {
editSenderID = ownerID;
editSenderIDSet = true;
} else {
if (ownerID < editSenderID) {
editSenderID = ownerID;
}
}
}
});
return editSenderID;
}

View file

@ -18,6 +18,7 @@
#include "AACube.h"
#include "SpatialParentFinder.h"
#include "shared/ReadWriteLockable.h"
#include "Grab.h"
class SpatiallyNestable;
using SpatiallyNestableWeakPointer = std::weak_ptr<SpatiallyNestable>;
@ -213,6 +214,11 @@ public:
virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed
virtual void parentDeleted() { } // called on children of a deleted parent
virtual void addGrab(GrabPointer grab);
virtual void removeGrab(GrabPointer grab);
bool hasGrabs();
virtual QUuid getEditSenderID();
protected:
QUuid _id;
mutable SpatiallyNestableWeakPointer _parent;
@ -232,6 +238,9 @@ protected:
quint64 _translationChanged { 0 };
quint64 _rotationChanged { 0 };
mutable ReadWriteLockable _grabsLock;
QSet<GrabPointer> _grabs;
private:
SpatiallyNestable() = delete;
const NestableType _nestableType; // EntityItem or an AvatarData

View file

@ -40,7 +40,8 @@ function updateOverlay(entityID, queryAACube) {
blue: 255
},
alpha: 1,
solid: false
solid: false,
grabbable: false
});
}
}
@ -60,4 +61,4 @@ function cleanup() {
}
}
Script.scriptEnding.connect(cleanup);
Script.scriptEnding.connect(cleanup);

View file

@ -0,0 +1,497 @@
"use strict";
// farGrabEntity.js
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* jslint bitwise: true */
/* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Quat, getEnabledModuleByName, makeRunningValues,
Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC,
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC,
projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, makeLaserParams, AddressManager,
getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, findGroupParent,
worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES
*/
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
Script.include("/~/system/libraries/controllers.js");
(function() {
var MARGIN = 25;
function TargetObject(entityID, entityProps) {
this.entityID = entityID;
this.entityProps = entityProps;
this.targetEntityID = null;
this.targetEntityProps = null;
this.getTargetEntity = function() {
var parentPropsLength = this.parentProps.length;
if (parentPropsLength !== 0) {
var targetEntity = {
id: this.parentProps[parentPropsLength - 1].id,
props: this.parentProps[parentPropsLength - 1]};
this.targetEntityID = targetEntity.id;
this.targetEntityProps = targetEntity.props;
return targetEntity;
}
this.targetEntityID = this.entityID;
this.targetEntityProps = this.entityProps;
return {
id: this.entityID,
props: this.entityProps};
};
}
function FarGrabEntity(hand) {
this.hand = hand;
this.grabbing = false;
this.targetEntityID = null;
this.targetObject = null;
this.previouslyUnhooked = {};
this.potentialEntityWithContextOverlay = false;
this.entityWithContextOverlay = false;
this.contextOverlayTimer = false;
this.reticleMinX = MARGIN;
this.reticleMaxX = 0;
this.reticleMinY = MARGIN;
this.reticleMaxY = 0;
this.endedGrab = 0;
this.MIN_HAPTIC_PULSE_INTERVAL = 500; // ms
var FAR_GRAB_JOINTS = [65527, 65528]; // FARGRAB_LEFTHAND_INDEX, FARGRAB_RIGHTHAND_INDEX
var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object
var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position
var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified
var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified
this.parameters = makeDispatcherModuleParameters(
540,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100,
makeLaserParams(this.hand, false));
this.handToController = function() {
return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
};
this.distanceGrabTimescale = function(mass, distance) {
var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass /
DISTANCE_HOLDING_UNITY_MASS * distance /
DISTANCE_HOLDING_UNITY_DISTANCE;
if (timeScale < DISTANCE_HOLDING_ACTION_TIMEFRAME) {
timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME;
}
return timeScale;
};
this.getMass = function(dimensions, density) {
return (dimensions.x * dimensions.y * dimensions.z) * density;
};
this.startFarGrabEntity = function (controllerData, targetProps) {
var controllerLocation = controllerData.controllerLocations[this.hand];
var worldControllerPosition = controllerLocation.position;
var worldControllerRotation = controllerLocation.orientation;
// transform the position into room space
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition);
var now = Date.now();
// add the action and initialize some variables
this.currentObjectPosition = targetProps.position;
this.currentObjectRotation = targetProps.rotation;
this.currentObjectTime = now;
this.grabRadius = this.grabbedDistance;
this.grabRadialVelocity = 0.0;
// offset between controller vector at the grab radius and the entity position
var targetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation));
targetPosition = Vec3.sum(targetPosition, worldControllerPosition);
this.offsetPosition = Vec3.subtract(this.currentObjectPosition, targetPosition);
// compute a constant based on the initial conditions which we use below to exaggerate hand motion
// onto the held object
this.radiusScalar = Math.log(this.grabRadius + 1.0);
if (this.radiusScalar < 1.0) {
this.radiusScalar = 1.0;
}
// compute the mass for the purpose of energy and how quickly to move object
this.mass = this.getMass(targetProps.dimensions, targetProps.density);
// Debounce haptic pules. Can occur as near grab controller module vacillates between being ready or not due to
// changing positions and floating point rounding.
if (Date.now() - this.endedGrab > this.MIN_HAPTIC_PULSE_INTERVAL) {
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
}
unhighlightTargetEntity(this.targetEntityID);
var message = {
hand: this.hand,
entityID: this.targetEntityID
};
Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message));
var newTargetPosLocal = MyAvatar.worldToJointPoint(targetProps.position);
MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], newTargetPosLocal);
MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], { x: 0, y: 0, z: 0, w: 1 });
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(targetProps.id, "startDistanceGrab", args);
this.targetEntityID = targetProps.id;
if (this.grabID) {
MyAvatar.releaseGrab(this.grabID);
}
var farJointIndex = FAR_GRAB_JOINTS[this.hand];
this.grabID = MyAvatar.grab(targetProps.id, farJointIndex,
Entities.worldToLocalPosition(targetProps.position, MyAvatar.SELF_ID, farJointIndex),
Entities.worldToLocalRotation(targetProps.rotation, MyAvatar.SELF_ID, farJointIndex));
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'grab',
grabbedEntity: targetProps.id,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
this.grabbing = true;
this.previousRoomControllerPosition = roomControllerPosition;
};
this.continueDistanceHolding = function(controllerData) {
var controllerLocation = controllerData.controllerLocations[this.hand];
var worldControllerPosition = controllerLocation.position;
var worldControllerRotation = controllerLocation.orientation;
// also transform the position into room space
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition);
var targetProps = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES);
var now = Date.now();
var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
this.currentObjectTime = now;
// the action was set up when this.distanceHolding was called. update the targets.
var radius = Vec3.distance(this.currentObjectPosition, worldControllerPosition) *
this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR;
if (radius < 1.0) {
radius = 1.0;
}
var roomHandDelta = Vec3.subtract(roomControllerPosition, this.previousRoomControllerPosition);
var worldHandDelta = Mat4.transformVector(MyAvatar.getSensorToWorldMatrix(), roomHandDelta);
var handMoved = Vec3.multiply(worldHandDelta, radius);
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved);
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.targetEntityID, "continueDistanceGrab", args);
// Update radialVelocity
var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime);
var delta = Vec3.normalize(Vec3.subtract(targetProps.position, worldControllerPosition));
var newRadialVelocity = Vec3.dot(lastVelocity, delta);
var VELOCITY_AVERAGING_TIME = 0.016;
var blendFactor = deltaObjectTime / VELOCITY_AVERAGING_TIME;
if (blendFactor < 0.0) {
blendFactor = 0.0;
} else if (blendFactor > 1.0) {
blendFactor = 1.0;
}
this.grabRadialVelocity = blendFactor * newRadialVelocity + (1.0 - blendFactor) * this.grabRadialVelocity;
var RADIAL_GRAB_AMPLIFIER = 10.0;
if (Math.abs(this.grabRadialVelocity) > 0.0) {
this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaObjectTime *
this.grabRadius * RADIAL_GRAB_AMPLIFIER);
}
// don't let grabRadius go all the way to zero, because it can't come back from that
var MINIMUM_GRAB_RADIUS = 0.1;
if (this.grabRadius < MINIMUM_GRAB_RADIUS) {
this.grabRadius = MINIMUM_GRAB_RADIUS;
}
var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation));
newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition);
newTargetPosition = Vec3.sum(newTargetPosition, this.offsetPosition);
// MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], MyAvatar.worldToJointPoint(newTargetPosition));
// var newTargetPosLocal = Mat4.transformPoint(MyAvatar.getSensorToWorldMatrix(), newTargetPosition);
var newTargetPosLocal = MyAvatar.worldToJointPoint(newTargetPosition);
MyAvatar.setJointTranslation(FAR_GRAB_JOINTS[this.hand], newTargetPosLocal);
MyAvatar.setJointRotation(FAR_GRAB_JOINTS[this.hand], { x: 0, y: 0, z: 0, w: 1 });
this.previousRoomControllerPosition = roomControllerPosition;
};
this.endFarGrabEntity = function (controllerData) {
if (this.grabID) {
MyAvatar.releaseGrab(this.grabID);
this.grabID = null;
}
this.endedGrab = Date.now();
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'release',
grabbedEntity: this.targetEntityID,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
unhighlightTargetEntity(this.targetEntityID);
this.grabbing = false;
this.targetEntityID = null;
this.potentialEntityWithContextOverlay = false;
MyAvatar.clearJointData(FAR_GRAB_JOINTS[this.hand]);
};
this.updateRecommendedArea = function() {
var dims = Controller.getViewportDimensions();
this.reticleMaxX = dims.x - MARGIN;
this.reticleMaxY = dims.y - MARGIN;
};
this.calculateNewReticlePosition = function(intersection) {
this.updateRecommendedArea();
var point2d = HMD.overlayFromWorldPoint(intersection);
point2d.x = Math.max(this.reticleMinX, Math.min(point2d.x, this.reticleMaxX));
point2d.y = Math.max(this.reticleMinY, Math.min(point2d.y, this.reticleMaxY));
return point2d;
};
this.notPointingAtEntity = function(controllerData) {
var intersection = controllerData.rayPicks[this.hand];
var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES);
var entityType = entityProperty.type;
var hudRayPick = controllerData.hudRayPicks[this.hand];
var point2d = this.calculateNewReticlePosition(hudRayPick.intersection);
if ((intersection.type === Picks.INTERSECTED_ENTITY && entityType === "Web") ||
intersection.type === Picks.INTERSECTED_OVERLAY || Window.isPointOnDesktopWindow(point2d)) {
return true;
}
return false;
};
this.destroyContextOverlay = function(controllerData) {
if (this.entityWithContextOverlay) {
ContextOverlay.destroyContextOverlay(this.entityWithContextOverlay);
this.entityWithContextOverlay = false;
this.potentialEntityWithContextOverlay = false;
}
};
this.targetIsNull = function() {
var properties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES);
if (Object.keys(properties).length === 0 && this.distanceHolding) {
return true;
}
return false;
};
this.getTargetProps = function (controllerData) {
var targetEntity = controllerData.rayPicks[this.hand].objectID;
if (targetEntity) {
var gtProps = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES);
if (entityIsGrabbable(gtProps)) {
// if we've attempted to grab a child, roll up to the root of the tree
var groupRootProps = findGroupParent(controllerData, gtProps);
if (entityIsGrabbable(groupRootProps)) {
return groupRootProps;
}
return gtProps;
}
}
return null;
};
this.isReady = function (controllerData) {
if (HMD.active) {
if (this.notPointingAtEntity(controllerData)) {
return makeRunningValues(false, [], []);
}
this.distanceHolding = false;
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
return makeRunningValues(true, [], []);
} else {
this.destroyContextOverlay();
}
}
return makeRunningValues(false, [], []);
};
this.run = function (controllerData) {
if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.targetIsNull()) {
this.endFarGrabEntity(controllerData);
return makeRunningValues(false, [], []);
}
this.intersectionDistance = controllerData.rayPicks[this.hand].distance;
// gather up the readiness of the near-grab modules
var nearGrabNames = [
this.hand === RIGHT_HAND ? "RightScaleAvatar" : "LeftScaleAvatar",
this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity",
this.hand === RIGHT_HAND ? "RightNearGrabEntity" : "LeftNearGrabEntity"
];
if (!this.grabbing) {
nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay");
nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight");
}
var nearGrabReadiness = [];
for (var i = 0; i < nearGrabNames.length; i++) {
var nearGrabModule = getEnabledModuleByName(nearGrabNames[i]);
var ready = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []);
nearGrabReadiness.push(ready);
}
if (this.targetEntityID) {
// if we are doing a distance grab and the object gets close enough to the controller,
// stop the far-grab so the near-grab or equip can take over.
for (var k = 0; k < nearGrabReadiness.length; k++) {
if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.targetEntityID)) {
this.endFarGrabEntity(controllerData);
return makeRunningValues(false, [], []);
}
}
this.continueDistanceHolding(controllerData);
} else {
// if we are doing a distance search and this controller moves into a position
// where it could near-grab something, stop searching.
for (var j = 0; j < nearGrabReadiness.length; j++) {
if (nearGrabReadiness[j].active) {
this.endFarGrabEntity(controllerData);
return makeRunningValues(false, [], []);
}
}
var rayPickInfo = controllerData.rayPicks[this.hand];
if (rayPickInfo.type === Picks.INTERSECTED_ENTITY) {
if (controllerData.triggerClicks[this.hand]) {
var entityID = rayPickInfo.objectID;
var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES);
if (targetProps.href !== "") {
AddressManager.handleLookupString(targetProps.href);
return makeRunningValues(false, [], []);
}
this.targetObject = new TargetObject(entityID, targetProps);
this.targetObject.parentProps = getEntityParents(targetProps);
if (this.contextOverlayTimer) {
Script.clearTimeout(this.contextOverlayTimer);
}
this.contextOverlayTimer = false;
if (entityID === this.entityWithContextOverlay) {
this.destroyContextOverlay();
} else {
Selection.removeFromSelectedItemsList("contextOverlayHighlightList", "entity", entityID);
}
var targetEntity = this.targetObject.getTargetEntity();
entityID = targetEntity.id;
targetProps = targetEntity.props;
if (entityIsGrabbable(targetProps) || entityIsGrabbable(this.targetObject.entityProps)) {
this.targetEntityID = entityID;
this.grabbedDistance = rayPickInfo.distance;
this.distanceHolding = true;
this.startFarGrabEntity(controllerData, targetProps);
}
} else if (!this.entityWithContextOverlay) {
var _this = this;
if (_this.potentialEntityWithContextOverlay !== rayPickInfo.objectID) {
if (_this.contextOverlayTimer) {
Script.clearTimeout(_this.contextOverlayTimer);
}
_this.contextOverlayTimer = false;
_this.potentialEntityWithContextOverlay = rayPickInfo.objectID;
}
if (!_this.contextOverlayTimer) {
_this.contextOverlayTimer = Script.setTimeout(function () {
if (!_this.entityWithContextOverlay &&
_this.contextOverlayTimer &&
_this.potentialEntityWithContextOverlay === rayPickInfo.objectID) {
var cotProps = Entities.getEntityProperties(rayPickInfo.objectID,
DISPATCHER_PROPERTIES);
var pointerEvent = {
type: "Move",
id: _this.hand + 1, // 0 is reserved for hardware mouse
pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID,
rayPickInfo.intersection, cotProps),
pos3D: rayPickInfo.intersection,
normal: rayPickInfo.surfaceNormal,
direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal),
button: "Secondary"
};
if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) {
_this.entityWithContextOverlay = rayPickInfo.objectID;
}
}
_this.contextOverlayTimer = false;
}, 500);
}
}
}
}
return this.exitIfDisabled(controllerData);
};
this.exitIfDisabled = function(controllerData) {
var moduleName = this.hand === RIGHT_HAND ? "RightDisableModules" : "LeftDisableModules";
var disableModule = getEnabledModuleByName(moduleName);
if (disableModule) {
if (disableModule.disableModules) {
this.endFarGrabEntity(controllerData);
Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity);
this.highlightedEntity = null;
return makeRunningValues(false, [], []);
}
}
var grabbedThing = this.distanceHolding ? this.targetObject.entityID : null;
var offset = this.calculateOffset(controllerData);
var laserLockInfo = makeLaserLockInfo(grabbedThing, false, this.hand, offset);
return makeRunningValues(true, [], [], laserLockInfo);
};
this.calculateOffset = function(controllerData) {
if (this.distanceHolding) {
var targetProps = Entities.getEntityProperties(this.targetObject.entityID,
[ "position", "rotation", "registrationPoint", "dimensions" ]);
return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection);
}
return undefined;
};
}
var leftFarGrabEntity = new FarGrabEntity(LEFT_HAND);
var rightFarGrabEntity = new FarGrabEntity(RIGHT_HAND);
enableDispatcherModule("LeftFarGrabEntity", leftFarGrabEntity);
enableDispatcherModule("RightFarGrabEntity", rightFarGrabEntity);
function cleanup() {
disableDispatcherModule("LeftFarGrabEntity");
disableDispatcherModule("RightFarGrabEntity");
}
Script.scriptEnding.connect(cleanup);
}());

View file

@ -8,10 +8,10 @@
/* 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,
MSECS_PER_SEC, makeDispatcherModuleParameters, makeRunningValues,
TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity,
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid,
DISPATCHER_PROPERTIES
DISPATCHER_PROPERTIES, HMD
*/
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
@ -65,25 +65,14 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
this.grabFollowsController = grabbableData.grabFollowsController;
this.kinematicGrab = grabbableData.grabKinematic;
var handRotation;
var handPosition;
if (this.grabFollowsController) {
var controllerID =
(this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var controllerLocation = getControllerWorldLocation(controllerID, false);
handRotation = controllerLocation.orientation;
handPosition = controllerLocation.position;
var handJointIndex;
if (HMD.mounted && HMD.isHandControllerAvailable() && grabbableData.grabFollowsController) {
handJointIndex = getControllerJointIndex(this.hand);
} else {
handRotation = this.getHandRotation();
handPosition = this.getHandPosition();
handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
}
var objectRotation = targetProps.rotation;
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
var currentObjectPosition = targetProps.position;
var offset = Vec3.subtract(currentObjectPosition, handPosition);
this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset);
this.offsetPosition = Entities.worldToLocalPosition(targetProps.position, MyAvatar.SELF_ID, handJointIndex);
this.offsetRotation = Entities.worldToLocalRotation(targetProps.rotation, MyAvatar.SELF_ID, handJointIndex);
var now = Date.now();
this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);

View file

@ -0,0 +1,225 @@
"use strict";
// nearGrabEntity.js
//
// 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, enableDispatcherModule,
disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE,
makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, cloneEntity,
entityIsCloneable, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, distanceBetweenPointAndEntityBoundingBox,
getGrabbableData, getEnabledModuleByName, DISPATCHER_PROPERTIES, HMD, NEAR_GRAB_DISTANCE
*/
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
Script.include("/~/system/libraries/cloneEntityUtils.js");
Script.include("/~/system/libraries/controllers.js");
(function() {
function NearGrabEntity(hand) {
this.hand = hand;
this.targetEntityID = null;
this.grabbing = false;
this.cloneAllowed = true;
this.grabID = null;
this.parameters = makeDispatcherModuleParameters(
500,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100);
this.startGrab = function (targetProps) {
if (this.grabID) {
MyAvatar.releaseGrab(this.grabID);
}
var grabData = getGrabbableData(targetProps);
var handJointIndex;
if (HMD.mounted && HMD.isHandControllerAvailable() && grabData.grabFollowsController) {
handJointIndex = getControllerJointIndex(this.hand);
} else {
handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
}
this.targetEntityID = targetProps.id;
var relativePosition = Entities.worldToLocalPosition(targetProps.position, MyAvatar.SELF_ID, handJointIndex);
var relativeRotation = Entities.worldToLocalRotation(targetProps.rotation, MyAvatar.SELF_ID, handJointIndex);
this.grabID = MyAvatar.grab(targetProps.id, handJointIndex, relativePosition, relativeRotation);
};
this.startNearGrabEntity = function (targetProps) {
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
this.startGrab(targetProps);
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(targetProps.id, "startNearGrab", args);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'grab',
grabbedEntity: targetProps.id,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
this.grabbing = true;
};
this.endGrab = function () {
if (this.grabID) {
MyAvatar.releaseGrab(this.grabID);
this.grabID = null;
}
};
this.endNearGrabEntity = function () {
this.endGrab();
this.grabbing = false;
this.targetEntityID = null;
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'release',
grabbedEntity: this.targetEntityID,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
};
this.getTargetProps = function (controllerData) {
// nearbyEntityProperties is already sorted by length from controller
var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand];
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
var nearGrabDistance = NEAR_GRAB_DISTANCE * sensorScaleFactor;
var nearGrabRadius = NEAR_GRAB_RADIUS * sensorScaleFactor;
for (var i = 0; i < nearbyEntityProperties.length; i++) {
var props = nearbyEntityProperties[i];
var grabPosition = controllerData.controllerLocations[this.hand].position; // Is offset from hand position.
var dist = distanceBetweenPointAndEntityBoundingBox(grabPosition, props);
var distance = Vec3.distance(grabPosition, props.position);
if ((dist > nearGrabDistance) ||
(distance > nearGrabRadius)) { // Only smallish entities can be near grabbed.
continue;
}
if (entityIsGrabbable(props) || entityIsCloneable(props)) {
if (!entityIsCloneable(props)) {
// if we've attempted to grab a non-cloneable child, roll up to the root of the tree
var groupRootProps = findGroupParent(controllerData, props);
if (entityIsGrabbable(groupRootProps)) {
return groupRootProps;
}
}
return props;
}
}
return null;
};
this.isReady = function (controllerData, deltaTime) {
this.targetEntityID = null;
this.grabbing = false;
if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE &&
controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) {
this.cloneAllowed = true;
return makeRunningValues(false, [], []);
}
var scaleModuleName = this.hand === RIGHT_HAND ? "RightScaleEntity" : "LeftScaleEntity";
var scaleModule = getEnabledModuleByName(scaleModuleName);
if (scaleModule.grabbedThingID || scaleModule.isReady(controllerData).active) {
// we're rescaling -- don't start a grab.
return makeRunningValues(false, [], []);
}
var targetProps = this.getTargetProps(controllerData);
if (targetProps) {
this.targetEntityID = targetProps.id;
return makeRunningValues(true, [this.targetEntityID], []);
} else {
return makeRunningValues(false, [], []);
}
};
this.run = function (controllerData, deltaTime) {
if (this.grabbing) {
if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE &&
controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) {
this.endNearGrabEntity();
return makeRunningValues(false, [], []);
}
var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID];
if (!props) {
props = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES);
if (!props) {
// entity was deleted
this.grabbing = false;
this.targetEntityID = null;
return makeRunningValues(false, [], []);
}
}
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args);
} else {
// still searching
var readiness = this.isReady(controllerData);
if (!readiness.active) {
return readiness;
}
if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) {
// switch to grab
var targetProps = this.getTargetProps(controllerData);
var targetCloneable = entityIsCloneable(targetProps);
if (targetCloneable) {
if (this.cloneAllowed) {
var cloneID = cloneEntity(targetProps);
if (cloneID !== null) {
var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES);
cloneProps.id = cloneID;
this.grabbing = true;
this.targetEntityID = cloneID;
this.startNearGrabEntity(cloneProps);
this.cloneAllowed = false; // prevent another clone call until inputs released
}
}
} else if (targetProps) {
this.grabbing = true;
this.startNearGrabEntity(targetProps);
}
}
}
return makeRunningValues(true, [this.targetEntityID], []);
};
this.cleanup = function () {
if (this.targetEntityID) {
this.endNearGrabEntity();
}
};
}
var leftNearGrabEntity = new NearGrabEntity(LEFT_HAND);
var rightNearGrabEntity = new NearGrabEntity(RIGHT_HAND);
enableDispatcherModule("LeftNearGrabEntity", leftNearGrabEntity);
enableDispatcherModule("RightNearGrabEntity", rightNearGrabEntity);
function cleanup() {
leftNearGrabEntity.cleanup();
rightNearGrabEntity.cleanup();
disableDispatcherModule("LeftNearGrabEntity");
disableDispatcherModule("RightNearGrabEntity");
}
Script.scriptEnding.connect(cleanup);
}());

View file

@ -7,7 +7,7 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global Script, Vec3, MyAvatar, Entities, RIGHT_HAND */
/* global Script, Vec3, MyAvatar, Entities, RIGHT_HAND, entityIsGrabbable */
(function() {
var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
@ -62,14 +62,19 @@
var otherHandTargetProps = otherModule.getTargetProps(controllerData);
if (thisHandTargetProps && otherHandTargetProps) {
if (thisHandTargetProps.id === otherHandTargetProps.id) {
if (!entityIsGrabbable(thisHandTargetProps)) {
return dispatcherUtils.makeRunningValues(false, [], []);
}
this.grabbedThingID = thisHandTargetProps.id;
this.scalingStartDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position,
controllerData.controllerLocations[this.otherHand()].position));
this.scalingStartDistance =
Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position,
controllerData.controllerLocations[this.otherHand()].position));
this.scalingStartDimensions = thisHandTargetProps.dimensions;
return dispatcherUtils.makeRunningValues(true, [], []);
}
}
}
this.grabbedThingID = false;
return dispatcherUtils.makeRunningValues(false, [], []);
};
@ -82,10 +87,11 @@
controllerData.controllerLocations[this.otherHand()].position));
var currentRescale = scalingCurrentDistance / this.scalingStartDistance;
var newDimensions = Vec3.multiply(currentRescale, this.scalingStartDimensions);
Entities.editEntity(this.grabbedThingID, { dimensions: newDimensions });
Entities.editEntity(this.grabbedThingID, { localDimensions: newDimensions });
}
return dispatcherUtils.makeRunningValues(true, [], []);
}
this.grabbedThingID = false;
return dispatcherUtils.makeRunningValues(false, [], []);
};
}

View file

@ -18,11 +18,7 @@ var CONTOLLER_SCRIPTS = [
"toggleAdvancedMovementForHandControllers.js",
"handTouch.js",
"controllerDispatcher.js",
"controllerModules/nearParentGrabEntity.js",
"controllerModules/nearParentGrabOverlay.js",
"controllerModules/nearActionGrabEntity.js",
"controllerModules/farActionGrabEntityDynOnly.js",
"controllerModules/farParentGrabEntity.js",
"controllerModules/stylusInput.js",
"controllerModules/equipEntity.js",
"controllerModules/nearTrigger.js",
@ -39,6 +35,16 @@ var CONTOLLER_SCRIPTS = [
"controllerModules/nearTabletHighlight.js"
];
if (Settings.getValue("useTraitsGrab", true)) {
CONTOLLER_SCRIPTS.push("controllerModules/nearGrabEntity.js");
CONTOLLER_SCRIPTS.push("controllerModules/farGrabEntity.js");
} else {
CONTOLLER_SCRIPTS.push("controllerModules/nearParentGrabEntity.js");
CONTOLLER_SCRIPTS.push("controllerModules/nearActionGrabEntity.js");
CONTOLLER_SCRIPTS.push("controllerModules/farActionGrabEntityDynOnly.js");
CONTOLLER_SCRIPTS.push("controllerModules/farParentGrabEntity.js");
}
var DEBUG_MENU_ITEM = "Debug defaultScripts.js";

View file

@ -24,6 +24,19 @@ const DELTA_X_MOVE_COLUMNS_THRESHOLD = 2;
const DELTA_X_COLUMN_SWAP_POSITION = 5;
const CERTIFIED_PLACEHOLDER = "** Certified **";
function decimalMegabytes(number) {
return number ? (number / BYTES_PER_MEGABYTE).toFixed(1) : "";
}
function displayIfNonZero(number) {
return number ? number : "";
}
function getFilename(url) {
let urlParts = url.split('/');
return urlParts[urlParts.length - 1];
}
const COLUMNS = {
type: {
columnHeader: "Type",
@ -79,6 +92,7 @@ const COLUMNS = {
dropdownLabel: "Texture Size",
propertyID: "texturesSize",
initialWidth: 0.10,
format: decimalMegabytes
},
hasTransparent: {
columnHeader: "&#xe00b;",
@ -605,19 +619,6 @@ function loaded() {
}));
}
function decimalMegabytes(number) {
return number ? (number / BYTES_PER_MEGABYTE).toFixed(1) : "";
}
function displayIfNonZero(number) {
return number ? number : "";
}
function getFilename(url) {
let urlParts = url.split('/');
return urlParts[urlParts.length - 1];
}
function updateEntityData(entityData) {
entities = [];
entitiesByID = {};
@ -639,7 +640,7 @@ function loaded() {
certificateID: entity.certificateID,
verticesCount: displayIfNonZero(entity.verticesCount),
texturesCount: displayIfNonZero(entity.texturesCount),
texturesSize: decimalMegabytes(entity.texturesSize),
texturesSize: entity.texturesSize,
hasTransparent: entity.hasTransparent,
isBaked: entity.isBaked,
drawCalls: displayIfNonZero(entity.drawCalls),
@ -874,7 +875,11 @@ function loaded() {
if (column.data.glyph) {
elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null;
} else {
elCell.innerHTML = itemData[column.data.propertyID];
let value = itemData[column.data.propertyID];
if (column.data.format) {
value = column.data.format(value);
}
elCell.innerHTML = value;
}
elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;";
elCell.className = createColumnClassName(column.columnID);

View file

@ -5,7 +5,7 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4,
/* global module, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4,
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,
@ -31,6 +31,8 @@
disableDispatcherModule:true,
getEnabledModuleByName:true,
getGrabbableData:true,
isAnothersAvatarEntity:true,
isAnothersChildEntity:true,
entityIsGrabbable:true,
entityIsDistanceGrabbable:true,
getControllerJointIndexCacheTime:true,
@ -104,7 +106,7 @@ TEAR_AWAY_CHECK_TIME = 0.15; // seconds, duration between checks
NEAR_GRAB_DISTANCE = 0.14; // Grab an entity if its bounding box is within this distance.
// Smaller than TEAR_AWAY_DISTANCE for hysteresis.
DISPATCHER_HOVERING_LIST = "dispactherHoveringList";
DISPATCHER_HOVERING_LIST = "dispatcherHoveringList";
DISPATCHER_HOVERING_STYLE = {
isOutlineSmooth: true,
outlineWidth: 0,
@ -144,6 +146,7 @@ DISPATCHER_PROPERTIES = [
"grab.grabFollowsController",
"grab.triggerable",
"grab.equippable",
"grab.grabDelegateToParent",
"grab.equippableLeftPosition",
"grab.equippableLeftRotation",
"grab.equippableRightPosition",
@ -151,7 +154,9 @@ DISPATCHER_PROPERTIES = [
"grab.equippableIndicatorURL",
"grab.equippableIndicatorScale",
"grab.equippableIndicatorOffset",
"userData"
"userData",
"avatarEntity",
"owningAvatarID"
];
// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step
@ -286,10 +291,44 @@ getGrabbableData = function (ggdProps) {
return grabbableData;
};
isAnothersAvatarEntity = function (iaaeProps) {
if (!iaaeProps.avatarEntity) {
return false;
}
if (iaaeProps.owningAvatarID === MyAvatar.sessionUUID) {
return false;
}
if (iaaeProps.owningAvatarID === MyAvatar.SELF_ID) {
return false;
}
return true;
};
isAnothersChildEntity = function (iaceProps) {
while (iaceProps.parentID && iaceProps.parentID !== Uuid.NULL) {
if (Entities.getNestableType(iaceProps.parentID) == "avatar") {
if (iaceProps.parentID == MyAvatar.SELF_ID || iaceProps.parentID == MyAvatar.sessionUUID) {
return false; // not another's, it's mine.
}
return true;
}
// Entities.getNestableType(iaceProps.parentID) == "entity") {
var parentProps = Entities.getEntityProperties(iaceProps.parentID, DISPATCHER_PROPERTIES);
if (!parentProps) {
break;
}
parentProps.id = iaceProps.parentID;
iaceProps = parentProps;
}
return false;
};
entityIsGrabbable = function (eigProps) {
var grabbable = getGrabbableData(eigProps).grabbable;
if (!grabbable ||
eigProps.locked ||
isAnothersAvatarEntity(eigProps) ||
isAnothersChildEntity(eigProps) ||
FORBIDDEN_GRAB_TYPES.indexOf(eigProps.type) >= 0) {
return false;
}
@ -331,17 +370,9 @@ getControllerJointIndex = function (hand) {
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 ?
var 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;
@ -409,7 +440,8 @@ ensureDynamic = function (entityID) {
};
findGroupParent = function (controllerData, targetProps) {
while (targetProps.parentID &&
while (targetProps.grab.grabDelegateToParent &&
targetProps.parentID &&
targetProps.parentID !== Uuid.NULL &&
Entities.getNestableType(targetProps.parentID) == "entity") {
var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES);