Merge branch 'master' of github.com:highfidelity/hifi into input-recorder
|
@ -5,7 +5,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
|||
You will need the following tools to build our Android targets.
|
||||
|
||||
* [cmake](http://www.cmake.org/download/) ~> 3.5.1
|
||||
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.5.1
|
||||
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.6.2
|
||||
* [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4
|
||||
* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d
|
||||
* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
//
|
||||
// AssignmentAction.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "AssignmentAction.h"
|
||||
|
||||
AssignmentAction::AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
EntityActionInterface(type, id),
|
||||
_data(QByteArray()),
|
||||
_active(false),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
AssignmentAction::~AssignmentAction() {
|
||||
}
|
||||
|
||||
void AssignmentAction::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
withReadLock([&]{
|
||||
simulation->removeAction(_id);
|
||||
simulation->applyActionChanges();
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray AssignmentAction::serialize() const {
|
||||
QByteArray result;
|
||||
withReadLock([&]{
|
||||
result = _data;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void AssignmentAction::deserialize(QByteArray serializedArguments) {
|
||||
withWriteLock([&]{
|
||||
_data = serializedArguments;
|
||||
});
|
||||
}
|
||||
|
||||
bool AssignmentAction::updateArguments(QVariantMap arguments) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::updateArguments called in assignment-client.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap AssignmentAction::getArguments() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getArguments called in assignment-client.";
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getPosition() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getPosition called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setPosition(glm::vec3 position) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setPosition called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::quat AssignmentAction::getRotation() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getRotation called in assignment-client.";
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
void AssignmentAction::setRotation(glm::quat rotation) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setRotation called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getLinearVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getLinearVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setLinearVelocity called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentAction::getAngularVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::getAngularVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentAction::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentAction::setAngularVelocity called in assignment-client.";
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// AssignmentActionFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AssignmentActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
return EntityActionPointer(new AssignmentAction(type, id, ownerEntity));
|
||||
}
|
||||
|
||||
EntityActionPointer AssignmentActionFactory::factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
|
||||
if (action) {
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer AssignmentActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedActionDataStream(data);
|
||||
EntityActionType type;
|
||||
QUuid id;
|
||||
|
||||
serializedActionDataStream >> type;
|
||||
serializedActionDataStream >> id;
|
||||
|
||||
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
|
||||
|
||||
if (action) {
|
||||
action->deserialize(data);
|
||||
}
|
||||
return action;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
//
|
||||
// AssignmentActionFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentActionFactory_h
|
||||
#define hifi_AssignmentActionFactory_h
|
||||
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "AssignmentAction.h"
|
||||
|
||||
class AssignmentActionFactory : public EntityActionFactoryInterface {
|
||||
public:
|
||||
AssignmentActionFactory() : EntityActionFactoryInterface() { }
|
||||
virtual ~AssignmentActionFactory() { }
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentActionFactory_h
|
|
@ -32,7 +32,7 @@
|
|||
#include <ResourceScriptingInterface.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
#include "AssignmentActionFactory.h"
|
||||
#include "AssignmentDynamicFactory.h"
|
||||
|
||||
#include "AssignmentClient.h"
|
||||
#include "AssignmentClientLogging.h"
|
||||
|
@ -63,8 +63,8 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
|
||||
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
|
||||
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||
auto dynamicFactory = DependencyManager::set<AssignmentDynamicFactory>();
|
||||
DependencyManager::set<ResourceScriptingInterface>();
|
||||
|
||||
// setup a thread for the NodeList and its PacketReceiver
|
||||
|
|
83
assignment-client/src/AssignmentDynamic.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// AssignmentDynamic.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "AssignmentDynamic.h"
|
||||
|
||||
AssignmentDynamic::AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
EntityDynamicInterface(type, id),
|
||||
_data(QByteArray()),
|
||||
_active(false),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
AssignmentDynamic::~AssignmentDynamic() {
|
||||
}
|
||||
|
||||
void AssignmentDynamic::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
withReadLock([&]{
|
||||
simulation->removeDynamic(_id);
|
||||
simulation->applyDynamicChanges();
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray AssignmentDynamic::serialize() const {
|
||||
QByteArray result;
|
||||
withReadLock([&]{
|
||||
result = _data;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void AssignmentDynamic::deserialize(QByteArray serializedArguments) {
|
||||
withWriteLock([&]{
|
||||
_data = serializedArguments;
|
||||
});
|
||||
}
|
||||
|
||||
bool AssignmentDynamic::updateArguments(QVariantMap arguments) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::updateArguments called in assignment-client.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariantMap AssignmentDynamic::getArguments() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getArguments called in assignment-client.";
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentDynamic::getPosition() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getPosition called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
glm::quat AssignmentDynamic::getRotation() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getRotation called in assignment-client.";
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentDynamic::getLinearVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getLinearVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::setLinearVelocity called in assignment-client.";
|
||||
}
|
||||
|
||||
glm::vec3 AssignmentDynamic::getAngularVelocity() {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::getAngularVelocity called in assignment-client.";
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void AssignmentDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
qDebug() << "UNEXPECTED -- AssignmentDynamic::setAngularVelocity called in assignment-client.";
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// AssignmentAction.h
|
||||
// AssignmentDynamic.h
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves 2015-6-19
|
||||
|
@ -8,21 +8,21 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html
|
||||
|
||||
#ifndef hifi_AssignmentAction_h
|
||||
#define hifi_AssignmentAction_h
|
||||
#ifndef hifi_AssignmentDynamic_h
|
||||
#define hifi_AssignmentDynamic_h
|
||||
|
||||
#include <QUuid>
|
||||
#include <EntityItem.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
|
||||
class AssignmentAction : public EntityActionInterface, public ReadWriteLockable {
|
||||
class AssignmentDynamic : public EntityDynamicInterface, public ReadWriteLockable {
|
||||
public:
|
||||
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentAction();
|
||||
AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AssignmentDynamic();
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
|
@ -38,9 +38,7 @@ private:
|
|||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition() override;
|
||||
virtual void setPosition(glm::vec3 position) override;
|
||||
virtual glm::quat getRotation() override;
|
||||
virtual void setRotation(glm::quat rotation) override;
|
||||
virtual glm::vec3 getLinearVelocity() override;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
|
@ -50,4 +48,4 @@ protected:
|
|||
EntityItemWeakPointer _ownerEntity;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentAction_h
|
||||
#endif // hifi_AssignmentDynamic_h
|
48
assignment-client/src/AssignmentDynamicFactory.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// AssignmentDynamcFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AssignmentDynamicFactory.h"
|
||||
|
||||
|
||||
EntityDynamicPointer assignmentDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
return EntityDynamicPointer(new AssignmentDynamic(type, id, ownerEntity));
|
||||
}
|
||||
|
||||
EntityDynamicPointer AssignmentDynamicFactory::factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
|
||||
if (dynamic) {
|
||||
bool ok = dynamic->updateArguments(arguments);
|
||||
if (ok) {
|
||||
return dynamic;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityDynamicPointer AssignmentDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedDynamicDataStream(data);
|
||||
EntityDynamicType type;
|
||||
QUuid id;
|
||||
|
||||
serializedDynamicDataStream >> type;
|
||||
serializedDynamicDataStream >> id;
|
||||
|
||||
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
|
||||
|
||||
if (dynamic) {
|
||||
dynamic->deserialize(data);
|
||||
}
|
||||
return dynamic;
|
||||
}
|
29
assignment-client/src/AssignmentDynamicFactory.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// AssignmentDynamicFactory.cpp
|
||||
// assignment-client/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-19
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentDynamicFactory_h
|
||||
#define hifi_AssignmentDynamicFactory_h
|
||||
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
#include "AssignmentDynamic.h"
|
||||
|
||||
class AssignmentDynamicFactory : public EntityDynamicFactoryInterface {
|
||||
public:
|
||||
AssignmentDynamicFactory() : EntityDynamicFactoryInterface() { }
|
||||
virtual ~AssignmentDynamicFactory() { }
|
||||
virtual EntityDynamicPointer factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentDynamicFactory_h
|
|
@ -435,23 +435,8 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return SharedNodePointer();
|
||||
}
|
||||
|
||||
QUuid hintNodeID;
|
||||
|
||||
// in case this is a node that's failing to connect
|
||||
// double check we don't have a node whose sockets match exactly already in the list
|
||||
limitedNodeList->eachNodeBreakable([&nodeConnection, &hintNodeID](const SharedNodePointer& node){
|
||||
if (node->getPublicSocket() == nodeConnection.publicSockAddr
|
||||
&& node->getLocalSocket() == nodeConnection.localSockAddr) {
|
||||
// we have a node that already has these exact sockets - this occurs if a node
|
||||
// is unable to connect to the domain
|
||||
hintNodeID = node->getUUID();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||
|
||||
// set the edit rights for this user
|
||||
newNode->setPermissions(userPerms);
|
||||
|
@ -479,11 +464,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return newNode;
|
||||
}
|
||||
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID) {
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
|
||||
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
|
||||
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
|
||||
|
||||
QUuid nodeID;
|
||||
|
||||
if (connectedPeer) {
|
||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||
nodeID = nodeConnection.connectUUID;
|
||||
|
@ -493,10 +479,8 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
|
|||
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||
}
|
||||
} else {
|
||||
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
|
||||
if (nodeID.isNull()) {
|
||||
nodeID = QUuid::createUuid();
|
||||
}
|
||||
// we got a connectUUID we didn't recognize, randomly generate a new one
|
||||
nodeID = QUuid::createUuid();
|
||||
}
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
|
|
@ -76,8 +76,7 @@ private:
|
|||
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature);
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID = QUuid());
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
|
||||
|
||||
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
#include "devices/Leapmotion.h"
|
||||
#include "DiscoverabilityManager.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "InterfaceActionFactory.h"
|
||||
#include "InterfaceDynamicFactory.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "LODManager.h"
|
||||
#include "ModelPackager.h"
|
||||
|
@ -463,7 +463,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, InterfaceDynamicFactory>();
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
|
||||
|
||||
// Set dependencies
|
||||
|
@ -517,7 +517,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<OffscreenUi>();
|
||||
DependencyManager::set<AutoUpdater>();
|
||||
DependencyManager::set<PathUtils>();
|
||||
DependencyManager::set<InterfaceActionFactory>();
|
||||
DependencyManager::set<InterfaceDynamicFactory>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
DependencyManager::set<MessagesClient>();
|
||||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
|
||||
|
@ -4444,7 +4444,7 @@ void Application::update(float deltaTime) {
|
|||
_entitySimulation->setObjectsToChange(stillNeedChange);
|
||||
});
|
||||
|
||||
_entitySimulation->applyActionChanges();
|
||||
_entitySimulation->applyDynamicChanges();
|
||||
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
@ -4454,8 +4454,8 @@ void Application::update(float deltaTime) {
|
|||
_physicsEngine->changeObjects(motionStates);
|
||||
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachAction([&](EntityActionPointer action) {
|
||||
action->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||
dynamic->prepareForPhysicsSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
|
@ -6763,11 +6763,6 @@ void Application::updateDisplayMode() {
|
|||
return;
|
||||
}
|
||||
|
||||
UserActivityLogger::getInstance().logAction("changed_display_mode", {
|
||||
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
|
||||
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" }
|
||||
});
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
// Make the switch atomic from the perspective of other threads
|
||||
|
@ -6822,13 +6817,16 @@ void Application::updateDisplayMode() {
|
|||
offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked);
|
||||
}
|
||||
|
||||
bool isHmd = _displayPlugin->isHmd();
|
||||
qCDebug(interfaceapp) << "Entering into" << (isHmd ? "HMD" : "Desktop") << "Mode";
|
||||
|
||||
// Only log/emit after a successful change
|
||||
UserActivityLogger::getInstance().logAction("changed_display_mode", {
|
||||
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
|
||||
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" },
|
||||
{ "hmd", isHmd }
|
||||
});
|
||||
emit activeDisplayPluginChanged();
|
||||
|
||||
if (_displayPlugin->isHmd()) {
|
||||
qCDebug(interfaceapp) << "Entering into HMD Mode";
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Entering into Desktop Mode";
|
||||
}
|
||||
|
||||
// reset the avatar, to set head and hand palms back to a reasonable default pose.
|
||||
getMyAvatar()->reset(false);
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
//
|
||||
// InterfaceActionFactory.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include <avatar/AvatarActionHold.h>
|
||||
#include <ObjectActionOffset.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
#include <ObjectActionTravelOriented.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "InterfaceActionFactory.h"
|
||||
|
||||
|
||||
EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
switch (type) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return EntityActionPointer();
|
||||
case ACTION_TYPE_OFFSET:
|
||||
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
|
||||
case ACTION_TYPE_SPRING:
|
||||
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
|
||||
case ACTION_TYPE_HOLD:
|
||||
return std::make_shared<AvatarActionHold>(id, ownerEntity);
|
||||
case ACTION_TYPE_TRAVEL_ORIENTED:
|
||||
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
|
||||
}
|
||||
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");
|
||||
return EntityActionPointer();
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
|
||||
if (action) {
|
||||
bool ok = action->updateArguments(arguments);
|
||||
if (ok) {
|
||||
if (action->lifetimeIsOver()) {
|
||||
return nullptr;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedArgumentStream(data);
|
||||
EntityActionType type;
|
||||
QUuid id;
|
||||
|
||||
serializedArgumentStream >> type;
|
||||
serializedArgumentStream >> id;
|
||||
|
||||
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
|
||||
|
||||
if (action) {
|
||||
action->deserialize(data);
|
||||
if (action->lifetimeIsOver()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during action creation.*");
|
||||
qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation --"
|
||||
<< action->getExpires() << "<" << usecTimestampNow();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
88
interface/src/InterfaceDynamicFactory.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// InterfaceDynamicFactory.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include <avatar/AvatarActionHold.h>
|
||||
#include <avatar/AvatarActionFarGrab.h>
|
||||
#include <ObjectActionOffset.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
#include <ObjectActionTravelOriented.h>
|
||||
#include <ObjectConstraintHinge.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "InterfaceDynamicFactory.h"
|
||||
|
||||
|
||||
EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
|
||||
switch (type) {
|
||||
case DYNAMIC_TYPE_NONE:
|
||||
return EntityDynamicPointer();
|
||||
case DYNAMIC_TYPE_OFFSET:
|
||||
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_SPRING:
|
||||
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_HOLD:
|
||||
return std::make_shared<AvatarActionHold>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_TRAVEL_ORIENTED:
|
||||
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_HINGE:
|
||||
return std::make_shared<ObjectConstraintHinge>(id, ownerEntity);
|
||||
case DYNAMIC_TYPE_FAR_GRAB:
|
||||
return std::make_shared<AvatarActionFarGrab>(id, ownerEntity);
|
||||
}
|
||||
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type");
|
||||
return EntityDynamicPointer();
|
||||
}
|
||||
|
||||
|
||||
EntityDynamicPointer InterfaceDynamicFactory::factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) {
|
||||
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
|
||||
if (dynamic) {
|
||||
bool ok = dynamic->updateArguments(arguments);
|
||||
if (ok) {
|
||||
if (dynamic->lifetimeIsOver()) {
|
||||
return nullptr;
|
||||
}
|
||||
return dynamic;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityDynamicPointer InterfaceDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||
QDataStream serializedArgumentStream(data);
|
||||
EntityDynamicType type;
|
||||
QUuid id;
|
||||
|
||||
serializedArgumentStream >> type;
|
||||
serializedArgumentStream >> id;
|
||||
|
||||
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
|
||||
|
||||
if (dynamic) {
|
||||
dynamic->deserialize(data);
|
||||
if (dynamic->lifetimeIsOver()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during dynamic creation.*");
|
||||
qDebug() << "InterfaceDynamicFactory::factoryBA lifetimeIsOver during dynamic creation --"
|
||||
<< dynamic->getExpires() << "<" << usecTimestampNow();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return dynamic;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// InterfaceActionFactory.cpp
|
||||
// InterfaceDynamicFactory.cpp
|
||||
// interface/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-10
|
||||
|
@ -9,21 +9,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_InterfaceActionFactory_h
|
||||
#define hifi_InterfaceActionFactory_h
|
||||
#ifndef hifi_InterfaceDynamicFactory_h
|
||||
#define hifi_InterfaceDynamicFactory_h
|
||||
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
|
||||
class InterfaceActionFactory : public EntityActionFactoryInterface {
|
||||
class InterfaceDynamicFactory : public EntityDynamicFactoryInterface {
|
||||
public:
|
||||
InterfaceActionFactory() : EntityActionFactoryInterface() { }
|
||||
virtual ~InterfaceActionFactory() { }
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
InterfaceDynamicFactory() : EntityDynamicFactoryInterface() { }
|
||||
virtual ~InterfaceDynamicFactory() { }
|
||||
virtual EntityDynamicPointer factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) override;
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
QByteArray data) override;
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceActionFactory_h
|
||||
#endif // hifi_InterfaceDynamicFactory_h
|
64
interface/src/avatar/AvatarActionFarGrab.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// AvatarActionFarGrab.cpp
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2017-4-14
|
||||
// Copyright 2017 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 "AvatarActionFarGrab.h"
|
||||
|
||||
AvatarActionFarGrab::AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity) {
|
||||
_type = DYNAMIC_TYPE_FAR_GRAB;
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionFarGrab::AvatarActionFarGrab";
|
||||
#endif
|
||||
}
|
||||
|
||||
AvatarActionFarGrab::~AvatarActionFarGrab() {
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionFarGrab::~AvatarActionFarGrab";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
QByteArray AvatarActionFarGrab::serialize() const {
|
||||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << DYNAMIC_TYPE_FAR_GRAB;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionSpring::springVersion;
|
||||
|
||||
serializeParameters(dataStream);
|
||||
|
||||
return serializedActionArguments;
|
||||
}
|
||||
|
||||
void AvatarActionFarGrab::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
|
||||
if (type != getType() || id != getID()) {
|
||||
qDebug() << "AvatarActionFarGrab::deserialize type or ID don't match." << type << id << getID();
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectActionSpring::springVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
deserializeParameters(serializedArguments, dataStream);
|
||||
}
|
27
interface/src/avatar/AvatarActionFarGrab.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// AvatarActionFarGrab.h
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Seth Alves 2017-4-14
|
||||
// Copyright 2017 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_AvatarActionFarGrab_h
|
||||
#define hifi_AvatarActionFarGrab_h
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include <ObjectActionSpring.h>
|
||||
|
||||
class AvatarActionFarGrab : public ObjectActionSpring {
|
||||
public:
|
||||
AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~AvatarActionFarGrab();
|
||||
|
||||
QByteArray serialize() const override;
|
||||
virtual void deserialize(QByteArray serializedArguments) override;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarActionFarGrab_h
|
|
@ -23,7 +23,7 @@ const int AvatarActionHold::velocitySmoothFrames = 6;
|
|||
AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectActionSpring(id, ownerEntity)
|
||||
{
|
||||
_type = ACTION_TYPE_HOLD;
|
||||
_type = DYNAMIC_TYPE_HOLD;
|
||||
_measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames);
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
@ -323,28 +323,28 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
bool ignoreIK;
|
||||
bool needUpdate = false;
|
||||
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
|
||||
relativePosition = EntityDynamicInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
|
||||
if (!ok) {
|
||||
relativePosition = _relativePosition;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
|
||||
relativeRotation = EntityDynamicInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
|
||||
if (!ok) {
|
||||
relativeRotation = _relativeRotation;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
|
||||
timeScale = EntityDynamicInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
|
||||
if (!ok) {
|
||||
timeScale = _linearTimeScale;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
|
||||
hand = EntityDynamicInterface::extractStringArgument("hold", arguments, "hand", ok, false);
|
||||
if (!ok || !(hand == "left" || hand == "right")) {
|
||||
hand = _hand;
|
||||
}
|
||||
|
@ -353,20 +353,20 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
holderID = myAvatar->getSessionUUID();
|
||||
|
||||
ok = true;
|
||||
kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
|
||||
kinematic = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
|
||||
if (!ok) {
|
||||
kinematic = _kinematic;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments,
|
||||
kinematicSetVelocity = EntityDynamicInterface::extractBooleanArgument("hold", arguments,
|
||||
"kinematicSetVelocity", ok, false);
|
||||
if (!ok) {
|
||||
kinematicSetVelocity = _kinematicSetVelocity;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
ignoreIK = EntityActionInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
|
||||
ignoreIK = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
|
||||
if (!ok) {
|
||||
ignoreIK = _ignoreIK;
|
||||
}
|
||||
|
@ -400,8 +400,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap AvatarActionHold::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&]{
|
||||
arguments["holderID"] = _holderID;
|
||||
arguments["relativePosition"] = glmToQMap(_relativePosition);
|
||||
|
@ -429,7 +429,7 @@ QByteArray AvatarActionHold::serialize() const {
|
|||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
withReadLock([&]{
|
||||
dataStream << ACTION_TYPE_HOLD;
|
||||
dataStream << DYNAMIC_TYPE_HOLD;
|
||||
dataStream << getID();
|
||||
dataStream << AvatarActionHold::holdVersion;
|
||||
|
||||
|
@ -451,7 +451,7 @@ QByteArray AvatarActionHold::serialize() const {
|
|||
void AvatarActionHold::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
|
|
|
@ -1,332 +0,0 @@
|
|||
//
|
||||
// EntityActionInterface.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-4
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
|
||||
+-----------------------+ +-------------------+ +------------------------------+
|
||||
| | | | | |
|
||||
| EntityActionInterface | | btActionInterface | | EntityActionFactoryInterface |
|
||||
| (entities) | | (bullet) | | (entities) |
|
||||
+-----------------------+ +-------------------+ +------------------------------+
|
||||
| | | | |
|
||||
+----+ +--+ +----------+ | |
|
||||
| | | | |
|
||||
+-------------------+ +--------------+ +------------------------+ +-------------------------+
|
||||
| | | | | | | |
|
||||
| AssignmentAction | | ObjectAction | | InterfaceActionFactory | | AssignmentActionFactory |
|
||||
|(assignment client)| | (physics) | | (interface) | | (assignment client) |
|
||||
+-------------------+ +--------------+ +------------------------+ +-------------------------+
|
||||
|
|
||||
|
|
||||
|
|
||||
+--------------------+
|
||||
| |
|
||||
| ObjectActionSpring |
|
||||
| (physics) |
|
||||
+--------------------+
|
||||
|
||||
|
||||
|
||||
|
||||
An action is a callback which is registered with bullet. An action is called-back every physics
|
||||
simulation step and can do whatever it wants with the various datastructures it has available. An
|
||||
action, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
|
||||
point by a spring.
|
||||
|
||||
In this system, an action is a property of an EntityItem (rather, an EntityItem has a property which
|
||||
encodes a list of actions). Each action has a type and some arguments. Actions can be created by a
|
||||
script or when receiving information via an EntityTree data-stream (either over the network or from an
|
||||
svo file).
|
||||
|
||||
In the interface, if an EntityItem has actions, this EntityItem will have pointers to ObjectAction
|
||||
subclass (like ObjectActionSpring) instantiations. Code in the entities library affects an action-object
|
||||
via the EntityActionInterface (which knows nothing about bullet). When the ObjectAction subclass
|
||||
instance is created, it is registered as an action with bullet. Bullet will call into code in this
|
||||
instance with the btActionInterface every physics-simulation step.
|
||||
|
||||
Because the action can exist next to the interface's EntityTree or the entity-server's EntityTree,
|
||||
parallel versions of the factories and actions are needed.
|
||||
|
||||
In an entity-server, any type of action is instantiated as an AssignmentAction. This action isn't called
|
||||
by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its
|
||||
arguments. This may change as we try to make the entity-server's simple physics simulation better, but
|
||||
right now the AssignmentAction class is a place-holder.
|
||||
|
||||
The action-objects are instantiated by singleton (dependecy) subclasses of EntityActionFactoryInterface.
|
||||
In the interface, the subclass is an InterfaceActionFactory and it will produce things like
|
||||
ObjectActionSpring. In an entity-server the subclass is an AssignmentActionFactory and it always
|
||||
produces AssignmentActions.
|
||||
|
||||
Depending on the action's type, it will have various arguments. When a script changes an argument of an
|
||||
action, the argument-holding member-variables of ObjectActionSpring (in this example) are updated and
|
||||
also serialized into _actionData in the EntityItem. Each subclass of ObjectAction knows how to serialize
|
||||
and deserialize its own arguments. _actionData is what gets sent over the wire or saved in an svo file.
|
||||
When a packet-reader receives data for _actionData, it will save it in the EntityItem; this causes the
|
||||
deserializer in the ObjectAction subclass to be called with the new data, thereby updating its argument
|
||||
variables. These argument variables are used by the code which is run when bullet does a callback.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
|
||||
|
||||
EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeString) {
|
||||
QString normalizedActionTypeString = actionTypeString.toLower().remove('-').remove('_');
|
||||
if (normalizedActionTypeString == "none") {
|
||||
return ACTION_TYPE_NONE;
|
||||
}
|
||||
if (normalizedActionTypeString == "offset") {
|
||||
return ACTION_TYPE_OFFSET;
|
||||
}
|
||||
if (normalizedActionTypeString == "spring") {
|
||||
return ACTION_TYPE_SPRING;
|
||||
}
|
||||
if (normalizedActionTypeString == "hold") {
|
||||
return ACTION_TYPE_HOLD;
|
||||
}
|
||||
if (normalizedActionTypeString == "traveloriented") {
|
||||
return ACTION_TYPE_TRAVEL_ORIENTED;
|
||||
}
|
||||
|
||||
qCDebug(entities) << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
|
||||
return ACTION_TYPE_NONE;
|
||||
}
|
||||
|
||||
QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
|
||||
switch(actionType) {
|
||||
case ACTION_TYPE_NONE:
|
||||
return "none";
|
||||
case ACTION_TYPE_OFFSET:
|
||||
return "offset";
|
||||
case ACTION_TYPE_SPRING:
|
||||
return "spring";
|
||||
case ACTION_TYPE_HOLD:
|
||||
return "hold";
|
||||
case ACTION_TYPE_TRAVEL_ORIENTED:
|
||||
return "travel-oriented";
|
||||
}
|
||||
assert(false);
|
||||
return "none";
|
||||
}
|
||||
|
||||
glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
if (!xOk || !yOk || !zOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z) {
|
||||
// at least one of the values is NaN
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
QVariant wV = resultVM["w"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
bool wOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
float w = wV.toFloat(&wOk);
|
||||
if (!xOk || !yOk || !zOk || !wOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName
|
||||
<< "must be a map with keys: x, y, z, and w of type float.";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z || w != w) {
|
||||
// at least one of the components is NaN!
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
return glm::normalize(glm::quat(w, x, y, z));
|
||||
}
|
||||
|
||||
float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
float value = variant.toFloat(&variantOk);
|
||||
|
||||
if (!variantOk || std::isnan(value)) {
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
int value = variant.toInt(&variantOk);
|
||||
|
||||
if (!variantOk) {
|
||||
ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return "";
|
||||
}
|
||||
return arguments[argumentName].toString();
|
||||
}
|
||||
|
||||
bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return false;
|
||||
}
|
||||
return arguments[argumentName].toBool();
|
||||
}
|
||||
|
||||
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType)
|
||||
{
|
||||
return stream << (quint16)entityActionType;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType)
|
||||
{
|
||||
quint16 actionTypeAsInt;
|
||||
stream >> actionTypeAsInt;
|
||||
entityActionType = (EntityActionType)actionTypeAsInt;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QString serializedActionsToDebugString(QByteArray data) {
|
||||
if (data.size() == 0) {
|
||||
return QString();
|
||||
}
|
||||
QVector<QByteArray> serializedActions;
|
||||
QDataStream serializedActionsStream(data);
|
||||
serializedActionsStream >> serializedActions;
|
||||
|
||||
QString result;
|
||||
foreach(QByteArray serializedAction, serializedActions) {
|
||||
QDataStream serializedActionStream(serializedAction);
|
||||
EntityActionType actionType;
|
||||
QUuid actionID;
|
||||
serializedActionStream >> actionType;
|
||||
serializedActionStream >> actionID;
|
||||
result += EntityActionInterface::actionTypeToString(actionType) + "-" + actionID.toString() + " ";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// EntityActionFactoryInterface.cpp
|
||||
// EntityDynamicFactoryInterface.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
|
@ -9,26 +9,26 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_EntityActionFactoryInterface_h
|
||||
#define hifi_EntityActionFactoryInterface_h
|
||||
#ifndef hifi_EntityDynamicFactoryInterface_h
|
||||
#define hifi_EntityDynamicFactoryInterface_h
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
class EntityActionFactoryInterface : public QObject, public Dependency {
|
||||
class EntityDynamicFactoryInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
EntityActionFactoryInterface() { }
|
||||
virtual ~EntityActionFactoryInterface() { }
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
EntityDynamicFactoryInterface() { }
|
||||
virtual ~EntityDynamicFactoryInterface() { }
|
||||
virtual EntityDynamicPointer factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) { assert(false); return nullptr; }
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity,
|
||||
QByteArray data) { assert(false); return nullptr; }
|
||||
};
|
||||
|
||||
#endif // hifi_EntityActionFactoryInterface_h
|
||||
#endif // hifi_EntityDynamicFactoryInterface_h
|
347
libraries/entities/src/EntityDynamicInterface.cpp
Normal file
|
@ -0,0 +1,347 @@
|
|||
//
|
||||
// EntityDynamicInterface.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-4
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
+-------------------------+ +--------------------------------+
|
||||
| | | |
|
||||
| EntityDynamicsInterface | | EntityDynamicsFactoryInterface |
|
||||
| (entities) | | (entities) |
|
||||
+-------------------------+ +--------------------------------+
|
||||
| | | |
|
||||
+----+ +--+ | |
|
||||
| | | |
|
||||
+---------------------+ +----------------+ +--------------------------+ +---------------------------+
|
||||
| | | | | | | |
|
||||
| AssignmentDynamics | | ObjectDynamics | | InterfaceDynamicsFactory | | AssignmentDynamicsFactory |
|
||||
|(assignment client) | | (physics) | | (interface) | | (assignment client) |
|
||||
+---------------------+ +----------------+ +--------------------------+ +---------------------------+
|
||||
| |
|
||||
| |
|
||||
+---------------------+ | |
|
||||
| | | |
|
||||
| btActionInterface | | |
|
||||
| (bullet) | | |
|
||||
+---------------------+ | |
|
||||
| | |
|
||||
+--------------+ +------------------+ +-------------------+
|
||||
| | | | | |
|
||||
| ObjectAction | | ObjectConstraint | | btTypedConstraint |
|
||||
| (physics) | | (physics) --|--->| (bullet) |
|
||||
+--------------+ +------------------+ +-------------------+
|
||||
| |
|
||||
| |
|
||||
+--------------------+ +-----------------------+
|
||||
| | | |
|
||||
| ObjectActionSpring | | ObjectConstraintHinge |
|
||||
| (physics) | | (physics) |
|
||||
+--------------------+ +-----------------------+
|
||||
|
||||
|
||||
|
||||
An dynamic is a callback which is registered with bullet. An dynamic is called-back every physics
|
||||
simulation step and can do whatever it wants with the various datastructures it has available. An
|
||||
dynamic, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
|
||||
point by a spring.
|
||||
|
||||
In this system, an dynamic is a property of an EntityItem (rather, an EntityItem has a property which
|
||||
encodes a list of dynamics). Each dynamic has a type and some arguments. Dynamics can be created by a
|
||||
script or when receiving information via an EntityTree data-stream (either over the network or from an
|
||||
svo file).
|
||||
|
||||
In the interface, if an EntityItem has dynamics, this EntityItem will have pointers to ObjectDynamic
|
||||
subclass (like ObjectDynamicSpring) instantiations. Code in the entities library affects an dynamic-object
|
||||
via the EntityDynamicInterface (which knows nothing about bullet). When the ObjectDynamic subclass
|
||||
instance is created, it is registered as an dynamic with bullet. Bullet will call into code in this
|
||||
instance with the btDynamicInterface every physics-simulation step.
|
||||
|
||||
Because the dynamic can exist next to the interface's EntityTree or the entity-server's EntityTree,
|
||||
parallel versions of the factories and dynamics are needed.
|
||||
|
||||
In an entity-server, any type of dynamic is instantiated as an AssignmentDynamic. This dynamic isn't called
|
||||
by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its
|
||||
arguments. This may change as we try to make the entity-server's simple physics simulation better, but
|
||||
right now the AssignmentDynamic class is a place-holder.
|
||||
|
||||
The dynamic-objects are instantiated by singleton (dependecy) subclasses of EntityDynamicFactoryInterface.
|
||||
In the interface, the subclass is an InterfaceDynamicFactory and it will produce things like
|
||||
ObjectDynamicSpring. In an entity-server the subclass is an AssignmentDynamicFactory and it always
|
||||
produces AssignmentDynamics.
|
||||
|
||||
Depending on the dynamic's type, it will have various arguments. When a script changes an argument of an
|
||||
dynamic, the argument-holding member-variables of ObjectDynamicSpring (in this example) are updated and
|
||||
also serialized into _dynamicData in the EntityItem. Each subclass of ObjectDynamic knows how to serialize
|
||||
and deserialize its own arguments. _dynamicData is what gets sent over the wire or saved in an svo file.
|
||||
When a packet-reader receives data for _dynamicData, it will save it in the EntityItem; this causes the
|
||||
deserializer in the ObjectDynamic subclass to be called with the new data, thereby updating its argument
|
||||
variables. These argument variables are used by the code which is run when bullet does a callback.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
|
||||
EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicTypeString) {
|
||||
QString normalizedDynamicTypeString = dynamicTypeString.toLower().remove('-').remove('_');
|
||||
if (normalizedDynamicTypeString == "none") {
|
||||
return DYNAMIC_TYPE_NONE;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "offset") {
|
||||
return DYNAMIC_TYPE_OFFSET;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "spring") {
|
||||
return DYNAMIC_TYPE_SPRING;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "hold") {
|
||||
return DYNAMIC_TYPE_HOLD;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "traveloriented") {
|
||||
return DYNAMIC_TYPE_TRAVEL_ORIENTED;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "hinge") {
|
||||
return DYNAMIC_TYPE_HINGE;
|
||||
}
|
||||
if (normalizedDynamicTypeString == "fargrab") {
|
||||
return DYNAMIC_TYPE_FAR_GRAB;
|
||||
}
|
||||
|
||||
qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name"
|
||||
<< dynamicTypeString;
|
||||
return DYNAMIC_TYPE_NONE;
|
||||
}
|
||||
|
||||
QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicType) {
|
||||
switch(dynamicType) {
|
||||
case DYNAMIC_TYPE_NONE:
|
||||
return "none";
|
||||
case DYNAMIC_TYPE_OFFSET:
|
||||
return "offset";
|
||||
case DYNAMIC_TYPE_SPRING:
|
||||
return "spring";
|
||||
case DYNAMIC_TYPE_HOLD:
|
||||
return "hold";
|
||||
case DYNAMIC_TYPE_TRAVEL_ORIENTED:
|
||||
return "travel-oriented";
|
||||
case DYNAMIC_TYPE_HINGE:
|
||||
return "hinge";
|
||||
case DYNAMIC_TYPE_FAR_GRAB:
|
||||
return "far-grab";
|
||||
}
|
||||
assert(false);
|
||||
return "none";
|
||||
}
|
||||
|
||||
glm::vec3 EntityDynamicInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
if (!xOk || !yOk || !zOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float.";
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z) {
|
||||
// at least one of the values is NaN
|
||||
ok = false;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
glm::quat EntityDynamicInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant resultV = arguments[argumentName];
|
||||
if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariantMap resultVM = resultV.toMap();
|
||||
if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
QVariant xV = resultVM["x"];
|
||||
QVariant yV = resultVM["y"];
|
||||
QVariant zV = resultVM["z"];
|
||||
QVariant wV = resultVM["w"];
|
||||
|
||||
bool xOk = true;
|
||||
bool yOk = true;
|
||||
bool zOk = true;
|
||||
bool wOk = true;
|
||||
float x = xV.toFloat(&xOk);
|
||||
float y = yV.toFloat(&yOk);
|
||||
float z = zV.toFloat(&zOk);
|
||||
float w = wV.toFloat(&wOk);
|
||||
if (!xOk || !yOk || !zOk || !wOk) {
|
||||
qCDebug(entities) << objectName << "argument" << argumentName
|
||||
<< "must be a map with keys: x, y, z, and w of type float.";
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
if (x != x || y != y || z != z || w != w) {
|
||||
// at least one of the components is NaN!
|
||||
ok = false;
|
||||
return glm::quat();
|
||||
}
|
||||
|
||||
return glm::normalize(glm::quat(w, x, y, z));
|
||||
}
|
||||
|
||||
float EntityDynamicInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
float value = variant.toFloat(&variantOk);
|
||||
|
||||
if (!variantOk || std::isnan(value)) {
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int EntityDynamicInterface::extractIntegerArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
QVariant variant = arguments[argumentName];
|
||||
bool variantOk = true;
|
||||
int value = variant.toInt(&variantOk);
|
||||
|
||||
if (!variantOk) {
|
||||
ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
QString EntityDynamicInterface::extractStringArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return "";
|
||||
}
|
||||
return arguments[argumentName].toString();
|
||||
}
|
||||
|
||||
bool EntityDynamicInterface::extractBooleanArgument(QString objectName, QVariantMap arguments,
|
||||
QString argumentName, bool& ok, bool required) {
|
||||
if (!arguments.contains(argumentName)) {
|
||||
if (required) {
|
||||
qCDebug(entities) << objectName << "requires argument:" << argumentName;
|
||||
}
|
||||
ok = false;
|
||||
return false;
|
||||
}
|
||||
return arguments[argumentName].toBool();
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType) {
|
||||
return stream << (quint16)entityDynamicType;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType) {
|
||||
quint16 dynamicTypeAsInt;
|
||||
stream >> dynamicTypeAsInt;
|
||||
entityDynamicType = (EntityDynamicType)dynamicTypeAsInt;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QString serializedDynamicsToDebugString(QByteArray data) {
|
||||
if (data.size() == 0) {
|
||||
return QString();
|
||||
}
|
||||
QVector<QByteArray> serializedDynamics;
|
||||
QDataStream serializedDynamicsStream(data);
|
||||
serializedDynamicsStream >> serializedDynamics;
|
||||
|
||||
QString result;
|
||||
foreach(QByteArray serializedDynamic, serializedDynamics) {
|
||||
QDataStream serializedDynamicStream(serializedDynamic);
|
||||
EntityDynamicType dynamicType;
|
||||
QUuid dynamicID;
|
||||
serializedDynamicStream >> dynamicType;
|
||||
serializedDynamicStream >> dynamicID;
|
||||
result += EntityDynamicInterface::dynamicTypeToString(dynamicType) + "-" + dynamicID.toString() + " ";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// EntityActionInterface.h
|
||||
// EntityDynamicInterface.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-2
|
||||
|
@ -9,8 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_EntityActionInterface_h
|
||||
#define hifi_EntityActionInterface_h
|
||||
#ifndef hifi_EntityDynamicInterface_h
|
||||
#define hifi_EntityDynamicInterface_h
|
||||
|
||||
#include <memory>
|
||||
#include <QUuid>
|
||||
|
@ -23,22 +23,27 @@ using EntityItemWeakPointer = std::weak_ptr<EntityItem>;
|
|||
class EntitySimulation;
|
||||
using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
|
||||
|
||||
enum EntityActionType {
|
||||
// keep these synchronized with actionTypeFromString and actionTypeToString
|
||||
ACTION_TYPE_NONE = 0,
|
||||
ACTION_TYPE_OFFSET = 1000,
|
||||
ACTION_TYPE_SPRING = 2000,
|
||||
ACTION_TYPE_HOLD = 3000,
|
||||
ACTION_TYPE_TRAVEL_ORIENTED = 4000
|
||||
enum EntityDynamicType {
|
||||
// keep these synchronized with dynamicTypeFromString and dynamicTypeToString
|
||||
DYNAMIC_TYPE_NONE = 0,
|
||||
DYNAMIC_TYPE_OFFSET = 1000,
|
||||
DYNAMIC_TYPE_SPRING = 2000,
|
||||
DYNAMIC_TYPE_HOLD = 3000,
|
||||
DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000,
|
||||
DYNAMIC_TYPE_HINGE = 5000,
|
||||
DYNAMIC_TYPE_FAR_GRAB = 6000
|
||||
};
|
||||
|
||||
|
||||
class EntityActionInterface {
|
||||
class EntityDynamicInterface {
|
||||
public:
|
||||
EntityActionInterface(EntityActionType type, const QUuid& id) : _id(id), _type(type) { }
|
||||
virtual ~EntityActionInterface() { }
|
||||
EntityDynamicInterface(EntityDynamicType type, const QUuid& id) : _id(id), _type(type) { }
|
||||
virtual ~EntityDynamicInterface() { }
|
||||
const QUuid& getID() const { return _id; }
|
||||
EntityActionType getType() const { return _type; }
|
||||
EntityDynamicType getType() const { return _type; }
|
||||
virtual bool isAction() const { return false; }
|
||||
virtual bool isConstraint() const { return false; }
|
||||
virtual bool isReadyForAdd() const { return true; }
|
||||
|
||||
bool isActive() { return _active; }
|
||||
|
||||
|
@ -51,8 +56,8 @@ public:
|
|||
virtual QByteArray serialize() const = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) = 0;
|
||||
|
||||
static EntityActionType actionTypeFromString(QString actionTypeString);
|
||||
static QString actionTypeToString(EntityActionType actionType);
|
||||
static EntityDynamicType dynamicTypeFromString(QString dynamicTypeString);
|
||||
static QString dynamicTypeToString(EntityDynamicType dynamicType);
|
||||
|
||||
virtual bool lifetimeIsOver() { return false; }
|
||||
virtual quint64 getExpires() { return 0; }
|
||||
|
@ -82,26 +87,26 @@ public:
|
|||
|
||||
protected:
|
||||
virtual glm::vec3 getPosition() = 0;
|
||||
virtual void setPosition(glm::vec3 position) = 0;
|
||||
// virtual void setPosition(glm::vec3 position) = 0;
|
||||
virtual glm::quat getRotation() = 0;
|
||||
virtual void setRotation(glm::quat rotation) = 0;
|
||||
// virtual void setRotation(glm::quat rotation) = 0;
|
||||
virtual glm::vec3 getLinearVelocity() = 0;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
|
||||
virtual glm::vec3 getAngularVelocity() = 0;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
|
||||
|
||||
QUuid _id;
|
||||
EntityActionType _type;
|
||||
EntityDynamicType _type;
|
||||
bool _active { false };
|
||||
bool _isMine { false }; // did this interface create / edit this action?
|
||||
bool _isMine { false }; // did this interface create / edit this dynamic?
|
||||
};
|
||||
|
||||
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType);
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType);
|
||||
QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType);
|
||||
QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType);
|
||||
|
||||
QString serializedActionsToDebugString(QByteArray data);
|
||||
QString serializedDynamicsToDebugString(QByteArray data);
|
||||
|
||||
#endif // hifi_EntityActionInterface_h
|
||||
#endif // hifi_EntityDynamicInterface_h
|
|
@ -30,7 +30,7 @@
|
|||
#include "EntitiesLogging.h"
|
||||
#include "EntityTree.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
|
||||
|
||||
int EntityItem::_maxActionsDataSize = 800;
|
||||
|
@ -280,7 +280,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getActionData());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getDynamicData());
|
||||
|
||||
// convert AVATAR_SELF_ID to actual sessionUUID.
|
||||
QUuid actualParentID = getParentID();
|
||||
|
@ -821,7 +821,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
|
||||
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
|
||||
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
|
||||
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setDynamicData);
|
||||
|
||||
{ // parentID and parentJointIndex are also protected by simulation ownership
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
|
@ -1251,7 +1251,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getActionData);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getDynamicData);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentID, getParentID);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentJointIndex, getParentJointIndex);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube);
|
||||
|
@ -1358,7 +1358,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setDynamicData);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, updateParentID);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube);
|
||||
|
@ -1872,10 +1872,20 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
|
|||
iAmHoldingThis = true;
|
||||
}
|
||||
// also, don't bootstrap our own avatar with a hold action
|
||||
QList<EntityActionPointer> holdActions = getActionsOfType(ACTION_TYPE_HOLD);
|
||||
QList<EntityActionPointer>::const_iterator i = holdActions.begin();
|
||||
QList<EntityDynamicPointer> holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD);
|
||||
QList<EntityDynamicPointer>::const_iterator i = holdActions.begin();
|
||||
while (i != holdActions.end()) {
|
||||
EntityActionPointer action = *i;
|
||||
EntityDynamicPointer action = *i;
|
||||
if (action->isMine()) {
|
||||
iAmHoldingThis = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
QList<EntityDynamicPointer> farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB);
|
||||
i = farGrabActions.begin();
|
||||
while (i != farGrabActions.end()) {
|
||||
EntityDynamicPointer action = *i;
|
||||
if (action->isMine()) {
|
||||
iAmHoldingThis = true;
|
||||
break;
|
||||
|
@ -1941,18 +1951,18 @@ void EntityItem::rememberHasSimulationOwnershipBid() const {
|
|||
QString EntityItem::actionsToDebugString() {
|
||||
QString result;
|
||||
QVector<QByteArray> serializedActions;
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
EntityActionPointer action = _objectActions[id];
|
||||
EntityActionType actionType = action->getType();
|
||||
EntityDynamicPointer action = _objectActions[id];
|
||||
EntityDynamicType actionType = action->getType();
|
||||
result += QString("") + actionType + ":" + action->getID().toString() + " ";
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPointer action) {
|
||||
bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPointer action) {
|
||||
bool result;
|
||||
withWriteLock([&] {
|
||||
checkWaitingToRemove(simulation);
|
||||
|
@ -1960,7 +1970,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint
|
|||
result = addActionInternal(simulation, action);
|
||||
if (result) {
|
||||
action->setIsMine(true);
|
||||
_actionDataDirty = true;
|
||||
_dynamicDataDirty = true;
|
||||
} else {
|
||||
removeActionInternal(action->getID());
|
||||
}
|
||||
|
@ -1969,7 +1979,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint
|
|||
return result;
|
||||
}
|
||||
|
||||
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action) {
|
||||
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action) {
|
||||
assert(action);
|
||||
assert(simulation);
|
||||
auto actionOwnerEntity = action->getOwnerEntity().lock();
|
||||
|
@ -1979,7 +1989,7 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityAct
|
|||
const QUuid& actionID = action->getID();
|
||||
assert(!_objectActions.contains(actionID) || _objectActions[actionID] == action);
|
||||
_objectActions[actionID] = action;
|
||||
simulation->addAction(action);
|
||||
simulation->addDynamic(action);
|
||||
|
||||
bool success;
|
||||
QByteArray newDataCache;
|
||||
|
@ -2003,14 +2013,13 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a
|
|||
return;
|
||||
}
|
||||
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
|
||||
success = action->updateArguments(arguments);
|
||||
if (success) {
|
||||
action->setIsMine(true);
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||
} else {
|
||||
qCDebug(entities) << "EntityItem::updateAction failed";
|
||||
}
|
||||
|
@ -2035,7 +2044,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
}
|
||||
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->setIsMine(false);
|
||||
|
@ -2049,7 +2058,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||
setActionDataNeedsTransmit(true);
|
||||
setDynamicDataNeedsTransmit(true);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
|
@ -2057,10 +2066,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
|
||||
bool EntityItem::clearActions(EntitySimulationPointer simulation) {
|
||||
withWriteLock([&] {
|
||||
QHash<QUuid, EntityActionPointer>::iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
EntityActionPointer action = _objectActions[id];
|
||||
EntityDynamicPointer action = _objectActions[id];
|
||||
i = _objectActions.erase(i);
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->removeFromSimulation(simulation);
|
||||
|
@ -2101,12 +2110,12 @@ void EntityItem::deserializeActionsInternal() {
|
|||
serializedActionsStream >> serializedActions;
|
||||
}
|
||||
|
||||
// Keep track of which actions got added or updated by the new actionData
|
||||
// Keep track of which actions got added or updated by the new dynamicData
|
||||
QSet<QUuid> updated;
|
||||
|
||||
foreach(QByteArray serializedAction, serializedActions) {
|
||||
QDataStream serializedActionStream(serializedAction);
|
||||
EntityActionType actionType;
|
||||
EntityDynamicType actionType;
|
||||
QUuid actionID;
|
||||
serializedActionStream >> actionType;
|
||||
serializedActionStream >> actionID;
|
||||
|
@ -2115,7 +2124,7 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
// TODO: make sure types match? there isn't currently a way to
|
||||
// change the type of an existing action.
|
||||
if (!action->isMine()) {
|
||||
|
@ -2123,9 +2132,9 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
updated << actionID;
|
||||
} else {
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
|
||||
EntityItemPointer entity = getThisPointer();
|
||||
EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
|
||||
EntityDynamicPointer action = actionFactory->factoryBA(entity, serializedAction);
|
||||
if (action) {
|
||||
entity->addActionInternal(simulation, action);
|
||||
updated << actionID;
|
||||
|
@ -2140,15 +2149,15 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
|
||||
// remove any actions that weren't included in the new data.
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
QUuid id = i.key();
|
||||
if (!updated.contains(id)) {
|
||||
EntityActionPointer action = i.value();
|
||||
EntityDynamicPointer action = i.value();
|
||||
|
||||
if (action->isMine()) {
|
||||
// we just received an update that didn't include one of our actions. tell the server about it (again).
|
||||
setActionDataNeedsTransmit(true);
|
||||
setDynamicDataNeedsTransmit(true);
|
||||
} else {
|
||||
// don't let someone else delete my action.
|
||||
_actionsToRemove << id;
|
||||
|
@ -2167,7 +2176,7 @@ void EntityItem::deserializeActionsInternal() {
|
|||
}
|
||||
}
|
||||
|
||||
_actionDataDirty = true;
|
||||
_dynamicDataDirty = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -2179,15 +2188,15 @@ void EntityItem::checkWaitingToRemove(EntitySimulationPointer simulation) {
|
|||
_actionsToRemove.clear();
|
||||
}
|
||||
|
||||
void EntityItem::setActionData(QByteArray actionData) {
|
||||
void EntityItem::setDynamicData(QByteArray dynamicData) {
|
||||
withWriteLock([&] {
|
||||
setActionDataInternal(actionData);
|
||||
setDynamicDataInternal(dynamicData);
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::setActionDataInternal(QByteArray actionData) {
|
||||
if (_allActionsDataCache != actionData) {
|
||||
_allActionsDataCache = actionData;
|
||||
void EntityItem::setDynamicDataInternal(QByteArray dynamicData) {
|
||||
if (_allActionsDataCache != dynamicData) {
|
||||
_allActionsDataCache = dynamicData;
|
||||
deserializeActionsInternal();
|
||||
}
|
||||
checkWaitingToRemove();
|
||||
|
@ -2201,10 +2210,10 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
|
|||
}
|
||||
|
||||
QVector<QByteArray> serializedActions;
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
const QUuid id = i.key();
|
||||
EntityActionPointer action = _objectActions[id];
|
||||
EntityDynamicPointer action = _objectActions[id];
|
||||
QByteArray bytesForAction = action->serialize();
|
||||
serializedActions << bytesForAction;
|
||||
i++;
|
||||
|
@ -2224,23 +2233,23 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
|
|||
return;
|
||||
}
|
||||
|
||||
const QByteArray EntityItem::getActionDataInternal() const {
|
||||
if (_actionDataDirty) {
|
||||
const QByteArray EntityItem::getDynamicDataInternal() const {
|
||||
if (_dynamicDataDirty) {
|
||||
bool success;
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
if (success) {
|
||||
_actionDataDirty = false;
|
||||
_dynamicDataDirty = false;
|
||||
}
|
||||
}
|
||||
return _allActionsDataCache;
|
||||
}
|
||||
|
||||
const QByteArray EntityItem::getActionData() const {
|
||||
const QByteArray EntityItem::getDynamicData() const {
|
||||
QByteArray result;
|
||||
|
||||
if (_actionDataDirty) {
|
||||
if (_dynamicDataDirty) {
|
||||
withWriteLock([&] {
|
||||
getActionDataInternal();
|
||||
getDynamicDataInternal();
|
||||
result = _allActionsDataCache;
|
||||
});
|
||||
} else {
|
||||
|
@ -2255,9 +2264,9 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
|
|||
QVariantMap result;
|
||||
withReadLock([&] {
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
result = action->getArguments();
|
||||
result["type"] = EntityActionInterface::actionTypeToString(action->getType());
|
||||
result["type"] = EntityDynamicInterface::dynamicTypeToString(action->getType());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2265,7 +2274,7 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
|
|||
}
|
||||
|
||||
bool EntityItem::shouldSuppressLocationEdits() const {
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
if (i.value()->shouldSuppressLocationEdits()) {
|
||||
return true;
|
||||
|
@ -2276,12 +2285,12 @@ bool EntityItem::shouldSuppressLocationEdits() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToGet) const {
|
||||
QList<EntityActionPointer> result;
|
||||
QList<EntityDynamicPointer> EntityItem::getActionsOfType(EntityDynamicType typeToGet) const {
|
||||
QList<EntityDynamicPointer> result;
|
||||
|
||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||
QHash<QUuid, EntityDynamicPointer>::const_iterator i = _objectActions.begin();
|
||||
while (i != _objectActions.end()) {
|
||||
EntityActionPointer action = i.value();
|
||||
EntityDynamicPointer action = i.value();
|
||||
if (action->getType() == typeToGet && action->isActive()) {
|
||||
result += action;
|
||||
}
|
||||
|
|
|
@ -36,17 +36,17 @@
|
|||
#include "EntityTypes.h"
|
||||
#include "SimulationOwner.h"
|
||||
#include "SimulationFlags.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
class EntitySimulation;
|
||||
class EntityTreeElement;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
class EntityActionInterface;
|
||||
class EntityDynamicInterface;
|
||||
class EntityItemProperties;
|
||||
class EntityTree;
|
||||
class btCollisionShape;
|
||||
typedef std::shared_ptr<EntityTree> EntityTreePointer;
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
typedef std::shared_ptr<EntityDynamicInterface> EntityDynamicPointer;
|
||||
typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
|
||||
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
|
||||
|
||||
|
@ -398,22 +398,22 @@ public:
|
|||
void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
|
||||
|
||||
QString actionsToDebugString();
|
||||
bool addAction(EntitySimulationPointer simulation, EntityActionPointer action);
|
||||
bool addAction(EntitySimulationPointer simulation, EntityDynamicPointer action);
|
||||
bool updateAction(EntitySimulationPointer simulation, const QUuid& actionID, const QVariantMap& arguments);
|
||||
bool removeAction(EntitySimulationPointer simulation, const QUuid& actionID);
|
||||
bool clearActions(EntitySimulationPointer simulation);
|
||||
void setActionData(QByteArray actionData);
|
||||
const QByteArray getActionData() const;
|
||||
void setDynamicData(QByteArray dynamicData);
|
||||
const QByteArray getDynamicData() const;
|
||||
bool hasActions() const { return !_objectActions.empty(); }
|
||||
QList<QUuid> getActionIDs() const { return _objectActions.keys(); }
|
||||
QVariantMap getActionArguments(const QUuid& actionID) const;
|
||||
void deserializeActions();
|
||||
|
||||
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
|
||||
bool actionDataDirty() const { return _actionDataDirty; }
|
||||
void setDynamicDataDirty(bool value) const { _dynamicDataDirty = value; }
|
||||
bool dynamicDataDirty() const { return _dynamicDataDirty; }
|
||||
|
||||
void setActionDataNeedsTransmit(bool value) const { _actionDataNeedsTransmit = value; }
|
||||
bool actionDataNeedsTransmit() const { return _actionDataNeedsTransmit; }
|
||||
void setDynamicDataNeedsTransmit(bool value) const { _dynamicDataNeedsTransmit = value; }
|
||||
bool dynamicDataNeedsTransmit() const { return _dynamicDataNeedsTransmit; }
|
||||
|
||||
bool shouldSuppressLocationEdits() const;
|
||||
|
||||
|
@ -421,7 +421,7 @@ public:
|
|||
const QUuid& getSourceUUID() const { return _sourceUUID; }
|
||||
bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; }
|
||||
|
||||
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet) const;
|
||||
QList<EntityDynamicPointer> getActionsOfType(EntityDynamicType typeToGet) const;
|
||||
|
||||
// these are in the frame of this object
|
||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
|
||||
|
@ -479,8 +479,8 @@ protected:
|
|||
|
||||
void setSimulated(bool simulated) { _simulated = simulated; }
|
||||
|
||||
const QByteArray getActionDataInternal() const;
|
||||
void setActionDataInternal(QByteArray actionData);
|
||||
const QByteArray getDynamicDataInternal() const;
|
||||
void setDynamicDataInternal(QByteArray dynamicData);
|
||||
|
||||
virtual void locationChanged(bool tellPhysics = true) override;
|
||||
virtual void dimensionsChanged() override;
|
||||
|
@ -570,22 +570,22 @@ protected:
|
|||
void* _physicsInfo { nullptr }; // set by EntitySimulation
|
||||
bool _simulated; // set by EntitySimulation
|
||||
|
||||
bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action);
|
||||
bool addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action);
|
||||
bool removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation = nullptr);
|
||||
void deserializeActionsInternal();
|
||||
void serializeActions(bool& success, QByteArray& result) const;
|
||||
QHash<QUuid, EntityActionPointer> _objectActions;
|
||||
QHash<QUuid, EntityDynamicPointer> _objectActions;
|
||||
|
||||
static int _maxActionsDataSize;
|
||||
mutable QByteArray _allActionsDataCache;
|
||||
|
||||
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
|
||||
// when an entity-server starts up, EntityItem::setDynamicData is called before the entity-tree is
|
||||
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
|
||||
// are used to keep track of and work around this situation.
|
||||
void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr);
|
||||
mutable QSet<QUuid> _actionsToRemove;
|
||||
mutable bool _actionDataDirty { false };
|
||||
mutable bool _actionDataNeedsTransmit { false };
|
||||
mutable bool _dynamicDataDirty { false };
|
||||
mutable bool _dynamicDataNeedsTransmit { false };
|
||||
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
|
||||
static quint64 _rememberDeletedActionTime;
|
||||
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include <model-networking/MeshProxy.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityActionFactoryInterface.h"
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "EntityTree.h"
|
||||
#include "LightEntityItem.h"
|
||||
|
@ -1133,7 +1133,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
QUuid actionID = QUuid::createUuid();
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
auto actionFactory = DependencyManager::get<EntityDynamicFactoryInterface>();
|
||||
bool success = false;
|
||||
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
// create this action even if the entity doesn't have physics info. it will often be the
|
||||
|
@ -1142,11 +1142,11 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
// if (!entity->getPhysicsInfo()) {
|
||||
// return false;
|
||||
// }
|
||||
EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
|
||||
if (actionType == ACTION_TYPE_NONE) {
|
||||
EntityDynamicType dynamicType = EntityDynamicInterface::dynamicTypeFromString(actionTypeString);
|
||||
if (dynamicType == DYNAMIC_TYPE_NONE) {
|
||||
return false;
|
||||
}
|
||||
EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments);
|
||||
EntityDynamicPointer action = actionFactory->factory(dynamicType, actionID, entity, arguments);
|
||||
if (!action) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -279,25 +279,25 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::addAction(EntityActionPointer action) {
|
||||
void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_actionsToAdd += action;
|
||||
_dynamicsToAdd += dynamic;
|
||||
}
|
||||
|
||||
void EntitySimulation::removeAction(const QUuid actionID) {
|
||||
void EntitySimulation::removeDynamic(const QUuid dynamicID) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_actionsToRemove += actionID;
|
||||
_dynamicsToRemove += dynamicID;
|
||||
}
|
||||
|
||||
void EntitySimulation::removeActions(QList<QUuid> actionIDsToRemove) {
|
||||
void EntitySimulation::removeDynamics(QList<QUuid> dynamicIDsToRemove) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
foreach(QUuid uuid, actionIDsToRemove) {
|
||||
_actionsToRemove.insert(uuid);
|
||||
foreach(QUuid uuid, dynamicIDsToRemove) {
|
||||
_dynamicsToRemove.insert(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::applyActionChanges() {
|
||||
void EntitySimulation::applyDynamicChanges() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_actionsToAdd.clear();
|
||||
_actionsToRemove.clear();
|
||||
_dynamicsToAdd.clear();
|
||||
_dynamicsToRemove.clear();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "EntityActionInterface.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
#include "EntityItem.h"
|
||||
#include "EntityTree.h"
|
||||
|
||||
|
@ -59,10 +59,10 @@ public:
|
|||
|
||||
// friend class EntityTree;
|
||||
|
||||
virtual void addAction(EntityActionPointer action);
|
||||
virtual void removeAction(const QUuid actionID);
|
||||
virtual void removeActions(QList<QUuid> actionIDsToRemove);
|
||||
virtual void applyActionChanges();
|
||||
virtual void addDynamic(EntityDynamicPointer dynamic);
|
||||
virtual void removeDynamic(const QUuid dynamicID);
|
||||
virtual void removeDynamics(QList<QUuid> dynamicIDsToRemove);
|
||||
virtual void applyDynamicChanges();
|
||||
|
||||
/// \param entity pointer to EntityItem to be added
|
||||
/// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
|
||||
|
@ -103,8 +103,8 @@ protected:
|
|||
|
||||
SetOfEntities _entitiesToSort; // entities moved by simulation (and might need resort in EntityTree)
|
||||
SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion
|
||||
QList<EntityActionPointer> _actionsToAdd;
|
||||
QSet<QUuid> _actionsToRemove;
|
||||
QList<EntityDynamicPointer> _dynamicsToAdd;
|
||||
QSet<QUuid> _dynamicsToRemove;
|
||||
|
||||
protected:
|
||||
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
|
||||
|
|
|
@ -808,7 +808,7 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
|
|||
int index = changedProperties.indexOf("actionData");
|
||||
if (index >= 0) {
|
||||
QByteArray value = properties.getActionData();
|
||||
QString changeHint = serializedActionsToDebugString(value);
|
||||
QString changeHint = serializedDynamicsToDebugString(value);
|
||||
changedProperties[index] = QString("actionData:") + changeHint;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,9 +69,7 @@ GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture&
|
|||
|
||||
GLuint GL41Texture::allocate(const Texture& texture) {
|
||||
GLuint result;
|
||||
// FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension
|
||||
glCreateTextures(getGLTextureType(texture), 1, &result);
|
||||
//glGenTextures(1, &result);
|
||||
glGenTextures(1, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -280,7 +278,7 @@ void GL41VariableAllocationTexture::promote() {
|
|||
|
||||
withPreservedTexture([&] {
|
||||
GLuint fbo { 0 };
|
||||
glCreateFramebuffers(1, &fbo);
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
|
||||
uint16_t mips = _gpuObject.getNumMips();
|
||||
|
|
|
@ -644,8 +644,6 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::unique_ptr<NLPacket> LimitedNodeList::constructPingPacket(PingType_t pingType) {
|
||||
int packetSize = sizeof(PingType_t) + sizeof(quint64);
|
||||
|
||||
|
|
|
@ -257,14 +257,14 @@ void NodeList::reset() {
|
|||
_avatarGainMap.clear();
|
||||
_avatarGainMapLock.unlock();
|
||||
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
setSessionUUID(QUuid());
|
||||
|
||||
if (sender() != &_domainHandler) {
|
||||
// clear the domain connection information, unless they're the ones that asked us to reset
|
||||
_domainHandler.softReset();
|
||||
}
|
||||
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
setSessionUUID(QUuid());
|
||||
|
||||
// if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking
|
||||
if (_dtlsSocket) {
|
||||
disconnect(_dtlsSocket, 0, this, 0);
|
||||
|
|
|
@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return VERSION_ENTITIES_ZONE_FILTERS;
|
||||
return VERSION_ENTITIES_HINGE_CONSTRAINT;
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -206,6 +206,7 @@ const PacketVersion VERSION_ENTITIES_LAST_EDITED_BY = 65;
|
|||
const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66;
|
||||
const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67;
|
||||
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
|
||||
const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
|
||||
|
||||
enum class EntityQueryPacketVersion: PacketVersion {
|
||||
JSONFilter = 18,
|
||||
|
|
|
@ -94,7 +94,7 @@ void EntityMotionState::updateServerPhysicsVariables() {
|
|||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
_serverActionData = _entity->getActionData();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
}
|
||||
|
||||
void EntityMotionState::handleDeactivation() {
|
||||
|
@ -309,7 +309,7 @@ bool EntityMotionState::isCandidateForOwnership() const {
|
|||
assert(entityTreeIsLocked());
|
||||
return _outgoingPriority != 0
|
||||
|| Physics::getSessionUUID() == _entity->getSimulatorID()
|
||||
|| _entity->actionDataNeedsTransmit();
|
||||
|| _entity->dynamicDataNeedsTransmit();
|
||||
}
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
|
@ -335,7 +335,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
_serverAcceleration = Vectors::ZERO;
|
||||
_serverAngularVelocity = worldVelocityToLocal.transform(bulletToGLM(_body->getAngularVelocity()));
|
||||
_lastStep = simulationStep;
|
||||
_serverActionData = _entity->getActionData();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
_numInactiveUpdates = 1;
|
||||
return false;
|
||||
}
|
||||
|
@ -387,7 +387,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
_outgoingPriority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
return true;
|
||||
}
|
||||
|
@ -474,7 +474,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -551,7 +551,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
_serverActionData = _entity->getActionData();
|
||||
_serverActionData = _entity->getDynamicData();
|
||||
|
||||
EntityItemProperties properties;
|
||||
|
||||
|
@ -562,8 +562,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
properties.setVelocity(_serverVelocity);
|
||||
properties.setAcceleration(_serverAcceleration);
|
||||
properties.setAngularVelocity(_serverAngularVelocity);
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
_entity->setActionDataNeedsTransmit(false);
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
_entity->setDynamicDataNeedsTransmit(false);
|
||||
properties.setActionData(_serverActionData);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,10 @@
|
|||
#include "PhysicsLogging.h"
|
||||
|
||||
|
||||
ObjectAction::ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction::ObjectAction(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
btActionInterface(),
|
||||
EntityActionInterface(type, id),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
ObjectAction::~ObjectAction() {
|
||||
ObjectDynamic(type, id, ownerEntity)
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
|
||||
|
@ -35,7 +32,7 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
|
|||
});
|
||||
|
||||
if (!ownerEntity) {
|
||||
qCDebug(physics) << "warning -- action with no entity removing self from btCollisionWorld.";
|
||||
qCDebug(physics) << "warning -- action [" << _tag << "] with no entity removing self from btCollisionWorld.";
|
||||
btDynamicsWorld* dynamicsWorld = static_cast<btDynamicsWorld*>(collisionWorld);
|
||||
if (dynamicsWorld) {
|
||||
dynamicsWorld->removeAction(this);
|
||||
|
@ -64,240 +61,5 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
|
|||
updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
|
||||
qint64 ObjectAction::getEntityServerClockSkew() const {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QUuid& entityServerNodeID = ownerEntity->getSourceUUID();
|
||||
auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID);
|
||||
if (entityServerNode) {
|
||||
return entityServerNode->getClockSkewUsec();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ObjectAction::updateArguments(QVariantMap arguments) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
withWriteLock([&]{
|
||||
quint64 previousExpires = _expires;
|
||||
QString previousTag = _tag;
|
||||
|
||||
bool ttlSet = true;
|
||||
float ttl = EntityActionInterface::extractFloatArgument("action", arguments, "ttl", ttlSet, false);
|
||||
if (ttlSet) {
|
||||
quint64 now = usecTimestampNow();
|
||||
_expires = now + (quint64)(ttl * USECS_PER_SECOND);
|
||||
} else {
|
||||
_expires = 0;
|
||||
}
|
||||
|
||||
bool tagSet = true;
|
||||
QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false);
|
||||
if (tagSet) {
|
||||
_tag = tag;
|
||||
} else {
|
||||
tag = "";
|
||||
}
|
||||
|
||||
if (previousExpires != _expires || previousTag != _tag) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
QVariantMap ObjectAction::getArguments() {
|
||||
QVariantMap arguments;
|
||||
withReadLock([&]{
|
||||
if (_expires == 0) {
|
||||
arguments["ttl"] = 0.0f;
|
||||
} else {
|
||||
quint64 now = usecTimestampNow();
|
||||
arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND;
|
||||
}
|
||||
arguments["tag"] = _tag;
|
||||
|
||||
EntityItemPointer entity = _ownerEntity.lock();
|
||||
if (entity) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
arguments["::active"] = motionState->isActive();
|
||||
arguments["::motion-type"] = motionTypeToString(motionState->getMotionType());
|
||||
} else {
|
||||
arguments["::no-motion-state"] = true;
|
||||
}
|
||||
}
|
||||
arguments["isMine"] = isMine();
|
||||
});
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
||||
void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
|
||||
}
|
||||
|
||||
void ObjectAction::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
QUuid myID;
|
||||
withReadLock([&]{
|
||||
myID = _id;
|
||||
});
|
||||
simulation->removeAction(myID);
|
||||
}
|
||||
|
||||
btRigidBody* ObjectAction::getRigidBody() {
|
||||
ObjectMotionState* motionState = nullptr;
|
||||
withReadLock([&]{
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
if (!physicsInfo) {
|
||||
return;
|
||||
}
|
||||
motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
});
|
||||
if (motionState) {
|
||||
return motionState->getRigidBody();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getPosition() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
}
|
||||
|
||||
void ObjectAction::setPosition(glm::vec3 position) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
// void setWorldTransform (const btTransform &worldTrans)
|
||||
assert(false);
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::quat ObjectAction::getRotation() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getOrientation());
|
||||
}
|
||||
|
||||
void ObjectAction::setRotation(glm::quat rotation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
// XXX
|
||||
// void setWorldTransform (const btTransform &worldTrans)
|
||||
assert(false);
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getLinearVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getLinearVelocity());
|
||||
}
|
||||
|
||||
void ObjectAction::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectAction::getAngularVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getAngularVelocity());
|
||||
}
|
||||
|
||||
void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
void ObjectAction::activateBody(bool forceActivation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (rigidBody) {
|
||||
rigidBody->activate(forceActivation);
|
||||
} else {
|
||||
qCDebug(physics) << "ObjectAction::activateBody -- no rigid body" << (void*)rigidBody;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectAction::forceBodyNonStatic() {
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
if (motionState && motionState->getMotionType() == MOTION_TYPE_STATIC) {
|
||||
ownerEntity->flagForMotionStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectAction::lifetimeIsOver() {
|
||||
if (_expires == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (now >= _expires) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 ObjectAction::localTimeToServerTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew < 0 && timeValue <= (quint64)(-serverClockSkew)) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue + serverClockSkew;
|
||||
}
|
||||
|
||||
quint64 ObjectAction::serverTimeToLocalTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew > 0 && timeValue <= (quint64)serverClockSkew) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue - serverClockSkew;
|
||||
}
|
||||
|
|
|
@ -14,27 +14,15 @@
|
|||
#define hifi_ObjectAction_h
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include "ObjectDynamic.h"
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
#include "EntityActionInterface.h"
|
||||
|
||||
|
||||
class ObjectAction : public btActionInterface, public EntityActionInterface, public ReadWriteLockable {
|
||||
class ObjectAction : public btActionInterface, public ObjectDynamic {
|
||||
public:
|
||||
ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectAction();
|
||||
ObjectAction(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectAction() {}
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; }
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
virtual bool isAction() const override { return true; }
|
||||
|
||||
// this is called from updateAction and should be overridden by subclasses
|
||||
virtual void updateActionWorker(float deltaTimeStep) = 0;
|
||||
|
@ -42,35 +30,6 @@ public:
|
|||
// these are from btActionInterface
|
||||
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) override;
|
||||
virtual void debugDraw(btIDebugDraw* debugDrawer) override;
|
||||
|
||||
virtual QByteArray serialize() const override = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) override = 0;
|
||||
|
||||
virtual bool lifetimeIsOver() override;
|
||||
virtual quint64 getExpires() override { return _expires; }
|
||||
|
||||
protected:
|
||||
quint64 localTimeToServerTime(quint64 timeValue) const;
|
||||
quint64 serverTimeToLocalTime(quint64 timeValue) const;
|
||||
|
||||
virtual btRigidBody* getRigidBody();
|
||||
virtual glm::vec3 getPosition() override;
|
||||
virtual void setPosition(glm::vec3 position) override;
|
||||
virtual glm::quat getRotation() override;
|
||||
virtual void setRotation(glm::quat rotation) override;
|
||||
virtual glm::vec3 getLinearVelocity() override;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
|
||||
virtual void activateBody(bool forceActivation = false);
|
||||
virtual void forceBodyNonStatic();
|
||||
|
||||
EntityItemWeakPointer _ownerEntity;
|
||||
QString _tag;
|
||||
quint64 _expires { 0 }; // in seconds since epoch
|
||||
|
||||
private:
|
||||
qint64 getEntityServerClockSkew() const;
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectAction_h
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
const uint16_t ObjectActionOffset::offsetVersion = 1;
|
||||
|
||||
ObjectActionOffset::ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction(ACTION_TYPE_OFFSET, id, ownerEntity),
|
||||
ObjectAction(DYNAMIC_TYPE_OFFSET, id, ownerEntity),
|
||||
_pointToOffsetFrom(0.0f),
|
||||
_linearDistance(0.0f),
|
||||
_linearTimeScale(FLT_MAX),
|
||||
|
@ -88,26 +88,26 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
float linearDistance;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
pointToOffsetFrom =
|
||||
EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
|
||||
EntityDynamicInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true);
|
||||
if (!ok) {
|
||||
pointToOffsetFrom = _pointToOffsetFrom;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
linearTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false);
|
||||
if (!ok) {
|
||||
linearTimeScale = _linearTimeScale;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
linearDistance =
|
||||
EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false);
|
||||
if (!ok) {
|
||||
linearDistance = _linearDistance;
|
||||
}
|
||||
|
@ -132,8 +132,8 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
@ -143,7 +143,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap ObjectActionOffset::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
arguments["pointToOffsetFrom"] = glmToQMap(_pointToOffsetFrom);
|
||||
arguments["linearTimeScale"] = _linearTimeScale;
|
||||
|
@ -155,7 +155,7 @@ QVariantMap ObjectActionOffset::getArguments() {
|
|||
QByteArray ObjectActionOffset::serialize() const {
|
||||
QByteArray ba;
|
||||
QDataStream dataStream(&ba, QIODevice::WriteOnly);
|
||||
dataStream << ACTION_TYPE_OFFSET;
|
||||
dataStream << DYNAMIC_TYPE_OFFSET;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionOffset::offsetVersion;
|
||||
|
||||
|
@ -174,7 +174,7 @@ QByteArray ObjectActionOffset::serialize() const {
|
|||
void ObjectActionOffset::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ const uint16_t ObjectActionSpring::springVersion = 1;
|
|||
|
||||
|
||||
ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction(ACTION_TYPE_SPRING, id, ownerEntity),
|
||||
ObjectAction(DYNAMIC_TYPE_SPRING, id, ownerEntity),
|
||||
_positionalTarget(glm::vec3(0.0f)),
|
||||
_desiredPositionalTarget(glm::vec3(0.0f)),
|
||||
_linearTimeScale(FLT_MAX),
|
||||
|
@ -64,11 +64,12 @@ bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) {
|
|||
bool valid = false;
|
||||
int springCount = 0;
|
||||
|
||||
QList<EntityActionPointer> springDerivedActions;
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_SPRING));
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_HOLD));
|
||||
QList<EntityDynamicPointer> springDerivedActions;
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_SPRING));
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_FAR_GRAB));
|
||||
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_HOLD));
|
||||
|
||||
foreach (EntityActionPointer action, springDerivedActions) {
|
||||
foreach (EntityDynamicPointer action, springDerivedActions) {
|
||||
std::shared_ptr<ObjectActionSpring> springAction = std::static_pointer_cast<ObjectActionSpring>(action);
|
||||
glm::quat rotationForAction;
|
||||
glm::vec3 positionForAction;
|
||||
|
@ -190,29 +191,29 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
|||
float angularTimeScale;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
// targets are required, spring-constants are optional
|
||||
bool ok = true;
|
||||
positionalTarget = EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
|
||||
positionalTarget = EntityDynamicInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
|
||||
if (!ok) {
|
||||
positionalTarget = _desiredPositionalTarget;
|
||||
}
|
||||
ok = true;
|
||||
linearTimeScale = EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
|
||||
linearTimeScale = EntityDynamicInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
|
||||
if (!ok || linearTimeScale <= 0.0f) {
|
||||
linearTimeScale = _linearTimeScale;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
rotationalTarget = EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
|
||||
rotationalTarget = EntityDynamicInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
|
||||
if (!ok) {
|
||||
rotationalTarget = _desiredRotationalTarget;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
angularTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
|
||||
if (!ok) {
|
||||
angularTimeScale = _angularTimeScale;
|
||||
}
|
||||
|
@ -237,8 +238,8 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
@ -248,7 +249,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap ObjectActionSpring::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
arguments["linearTimeScale"] = _linearTimeScale;
|
||||
arguments["targetPosition"] = glmToQMap(_desiredPositionalTarget);
|
||||
|
@ -259,14 +260,7 @@ QVariantMap ObjectActionSpring::getArguments() {
|
|||
return arguments;
|
||||
}
|
||||
|
||||
QByteArray ObjectActionSpring::serialize() const {
|
||||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << ACTION_TYPE_SPRING;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionSpring::springVersion;
|
||||
|
||||
void ObjectActionSpring::serializeParameters(QDataStream& dataStream) const {
|
||||
withReadLock([&] {
|
||||
dataStream << _desiredPositionalTarget;
|
||||
dataStream << _linearTimeScale;
|
||||
|
@ -277,28 +271,22 @@ QByteArray ObjectActionSpring::serialize() const {
|
|||
dataStream << localTimeToServerTime(_expires);
|
||||
dataStream << _tag;
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray ObjectActionSpring::serialize() const {
|
||||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << DYNAMIC_TYPE_SPRING;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionSpring::springVersion;
|
||||
|
||||
serializeParameters(dataStream);
|
||||
|
||||
return serializedActionArguments;
|
||||
}
|
||||
|
||||
void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
assert(id == getID());
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectActionSpring::springVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
void ObjectActionSpring::deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream) {
|
||||
withWriteLock([&] {
|
||||
dataStream >> _desiredPositionalTarget;
|
||||
dataStream >> _linearTimeScale;
|
||||
|
@ -317,3 +305,24 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
|
|||
_active = true;
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
assert(id == getID());
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectActionSpring::springVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
deserializeParameters(serializedArguments, dataStream);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,9 @@ protected:
|
|||
glm::vec3 _angularVelocityTarget;
|
||||
|
||||
virtual bool prepareForSpringUpdate(btScalar deltaTimeStep);
|
||||
|
||||
void serializeParameters(QDataStream& dataStream) const;
|
||||
void deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream);
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectActionSpring_h
|
||||
|
|
|
@ -19,7 +19,7 @@ const uint16_t ObjectActionTravelOriented::actionVersion = 1;
|
|||
|
||||
|
||||
ObjectActionTravelOriented::ObjectActionTravelOriented(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectAction(ACTION_TYPE_TRAVEL_ORIENTED, id, ownerEntity) {
|
||||
ObjectAction(DYNAMIC_TYPE_TRAVEL_ORIENTED, id, ownerEntity) {
|
||||
#if WANT_DEBUG
|
||||
qCDebug(physics) << "ObjectActionTravelOriented::ObjectActionTravelOriented";
|
||||
#endif
|
||||
|
@ -106,16 +106,16 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) {
|
|||
float angularTimeScale;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectAction::updateArguments(arguments);
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
forward = EntityActionInterface::extractVec3Argument("travel oriented action", arguments, "forward", ok, true);
|
||||
forward = EntityDynamicInterface::extractVec3Argument("travel oriented action", arguments, "forward", ok, true);
|
||||
if (!ok) {
|
||||
forward = _forward;
|
||||
}
|
||||
ok = true;
|
||||
angularTimeScale =
|
||||
EntityActionInterface::extractFloatArgument("travel oriented action", arguments, "angularTimeScale", ok, false);
|
||||
EntityDynamicInterface::extractFloatArgument("travel oriented action", arguments, "angularTimeScale", ok, false);
|
||||
if (!ok) {
|
||||
angularTimeScale = _angularTimeScale;
|
||||
}
|
||||
|
@ -136,8 +136,8 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) {
|
|||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
@ -147,7 +147,7 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) {
|
|||
}
|
||||
|
||||
QVariantMap ObjectActionTravelOriented::getArguments() {
|
||||
QVariantMap arguments = ObjectAction::getArguments();
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
arguments["forward"] = glmToQMap(_forward);
|
||||
arguments["angularTimeScale"] = _angularTimeScale;
|
||||
|
@ -159,7 +159,7 @@ QByteArray ObjectActionTravelOriented::serialize() const {
|
|||
QByteArray serializedActionArguments;
|
||||
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << ACTION_TYPE_TRAVEL_ORIENTED;
|
||||
dataStream << DYNAMIC_TYPE_TRAVEL_ORIENTED;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectActionTravelOriented::actionVersion;
|
||||
|
||||
|
@ -177,7 +177,7 @@ QByteArray ObjectActionTravelOriented::serialize() const {
|
|||
void ObjectActionTravelOriented::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityActionType type;
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
|
|
25
libraries/physics/src/ObjectConstraint.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// ObjectConstraint.cpp
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "ObjectConstraint.h"
|
||||
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
ObjectConstraint::ObjectConstraint(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectDynamic(type, id, ownerEntity)
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectConstraint::invalidate() {
|
||||
_constraint = nullptr;
|
||||
}
|
35
libraries/physics/src/ObjectConstraint.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// ObjectConstraint.h
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2017-4-11
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtConstraintInterface.html
|
||||
|
||||
#ifndef hifi_ObjectConstraint_h
|
||||
#define hifi_ObjectConstraint_h
|
||||
|
||||
#include <QUuid>
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include "ObjectDynamic.h"
|
||||
|
||||
class ObjectConstraint : public ObjectDynamic
|
||||
{
|
||||
public:
|
||||
ObjectConstraint(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectConstraint() {}
|
||||
|
||||
virtual btTypedConstraint* getConstraint() = 0;
|
||||
|
||||
virtual bool isConstraint() const override { return true; }
|
||||
virtual void invalidate() override;
|
||||
|
||||
protected:
|
||||
btTypedConstraint* _constraint { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectConstraint_h
|
363
libraries/physics/src/ObjectConstraintHinge.cpp
Normal file
|
@ -0,0 +1,363 @@
|
|||
//
|
||||
// ObjectConstraintHinge.cpp
|
||||
// libraries/physics/src
|
||||
//
|
||||
// Created by Seth Alves 2017-4-11
|
||||
// Copyright 2017 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 "QVariantGLM.h"
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "ObjectConstraintHinge.h"
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
|
||||
const uint16_t ObjectConstraintHinge::constraintVersion = 1;
|
||||
|
||||
|
||||
ObjectConstraintHinge::ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
ObjectConstraint(DYNAMIC_TYPE_HINGE, id, ownerEntity),
|
||||
_pivotInA(glm::vec3(0.0f)),
|
||||
_axisInA(glm::vec3(0.0f))
|
||||
{
|
||||
#if WANT_DEBUG
|
||||
qCDebug(physics) << "ObjectConstraintHinge::ObjectConstraintHinge";
|
||||
#endif
|
||||
}
|
||||
|
||||
ObjectConstraintHinge::~ObjectConstraintHinge() {
|
||||
#if WANT_DEBUG
|
||||
qCDebug(physics) << "ObjectConstraintHinge::~ObjectConstraintHinge";
|
||||
#endif
|
||||
}
|
||||
|
||||
QList<btRigidBody*> ObjectConstraintHinge::getRigidBodies() {
|
||||
QList<btRigidBody*> result;
|
||||
result += getRigidBody();
|
||||
QUuid otherEntityID;
|
||||
withReadLock([&]{
|
||||
otherEntityID = _otherEntityID;
|
||||
});
|
||||
if (!otherEntityID.isNull()) {
|
||||
result += getOtherRigidBody(otherEntityID);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ObjectConstraintHinge::updateHinge() {
|
||||
btHingeConstraint* constraint { nullptr };
|
||||
float low;
|
||||
float high;
|
||||
float softness;
|
||||
float biasFactor;
|
||||
float relaxationFactor;
|
||||
float motorVelocity;
|
||||
|
||||
withReadLock([&]{
|
||||
constraint = static_cast<btHingeConstraint*>(_constraint);
|
||||
low = _low;
|
||||
high = _high;
|
||||
softness = _softness;
|
||||
biasFactor = _biasFactor;
|
||||
relaxationFactor = _relaxationFactor;
|
||||
motorVelocity = _motorVelocity;
|
||||
});
|
||||
|
||||
if (!constraint) {
|
||||
return;
|
||||
}
|
||||
|
||||
constraint->setLimit(low, high, softness, biasFactor, relaxationFactor);
|
||||
if (motorVelocity != 0.0f) {
|
||||
constraint->setMotorTargetVelocity(motorVelocity);
|
||||
constraint->enableMotor(true);
|
||||
} else {
|
||||
constraint->enableMotor(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
btTypedConstraint* ObjectConstraintHinge::getConstraint() {
|
||||
btHingeConstraint* constraint { nullptr };
|
||||
QUuid otherEntityID;
|
||||
glm::vec3 pivotInA;
|
||||
glm::vec3 axisInA;
|
||||
glm::vec3 pivotInB;
|
||||
glm::vec3 axisInB;
|
||||
|
||||
withReadLock([&]{
|
||||
constraint = static_cast<btHingeConstraint*>(_constraint);
|
||||
pivotInA = _pivotInA;
|
||||
axisInA = _axisInA;
|
||||
otherEntityID = _otherEntityID;
|
||||
pivotInB = _pivotInB;
|
||||
axisInB = _axisInB;
|
||||
});
|
||||
if (constraint) {
|
||||
return constraint;
|
||||
}
|
||||
|
||||
btRigidBody* rigidBodyA = getRigidBody();
|
||||
if (!rigidBodyA) {
|
||||
qCDebug(physics) << "ObjectConstraintHinge::getConstraint -- no rigidBodyA";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!otherEntityID.isNull()) {
|
||||
// This hinge is between two entities... find the other rigid body.
|
||||
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
|
||||
if (!rigidBodyB) {
|
||||
return nullptr;
|
||||
}
|
||||
constraint = new btHingeConstraint(*rigidBodyA, *rigidBodyB,
|
||||
glmToBullet(pivotInA), glmToBullet(pivotInB),
|
||||
glmToBullet(axisInA), glmToBullet(axisInB),
|
||||
true); // useReferenceFrameA
|
||||
|
||||
} else {
|
||||
// This hinge is between an entity and the world-frame.
|
||||
constraint = new btHingeConstraint(*rigidBodyA,
|
||||
glmToBullet(pivotInA), glmToBullet(axisInA),
|
||||
true); // useReferenceFrameA
|
||||
}
|
||||
|
||||
withWriteLock([&]{
|
||||
_constraint = constraint;
|
||||
});
|
||||
|
||||
// if we don't wake up rigidBodyA, we may not send the dynamicData property over the network
|
||||
forceBodyNonStatic();
|
||||
activateBody();
|
||||
|
||||
updateHinge();
|
||||
|
||||
return constraint;
|
||||
}
|
||||
|
||||
|
||||
bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) {
|
||||
glm::vec3 pivotInA;
|
||||
glm::vec3 axisInA;
|
||||
QUuid otherEntityID;
|
||||
glm::vec3 pivotInB;
|
||||
glm::vec3 axisInB;
|
||||
float low;
|
||||
float high;
|
||||
float softness;
|
||||
float biasFactor;
|
||||
float relaxationFactor;
|
||||
float motorVelocity;
|
||||
|
||||
bool needUpdate = false;
|
||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||
withReadLock([&]{
|
||||
bool ok = true;
|
||||
pivotInA = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "pivot", ok, false);
|
||||
if (!ok) {
|
||||
pivotInA = _pivotInA;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
axisInA = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "axis", ok, false);
|
||||
if (!ok) {
|
||||
axisInA = _axisInA;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("hinge constraint",
|
||||
arguments, "otherEntityID", ok, false));
|
||||
if (!ok) {
|
||||
otherEntityID = _otherEntityID;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
pivotInB = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "otherPivot", ok, false);
|
||||
if (!ok) {
|
||||
pivotInB = _pivotInB;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
axisInB = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "otherAxis", ok, false);
|
||||
if (!ok) {
|
||||
axisInB = _axisInB;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
low = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "low", ok, false);
|
||||
if (!ok) {
|
||||
low = _low;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
high = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "high", ok, false);
|
||||
if (!ok) {
|
||||
high = _high;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
softness = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "softness", ok, false);
|
||||
if (!ok) {
|
||||
softness = _softness;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
biasFactor = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "biasFactor", ok, false);
|
||||
if (!ok) {
|
||||
biasFactor = _biasFactor;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
relaxationFactor = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments,
|
||||
"relaxationFactor", ok, false);
|
||||
if (!ok) {
|
||||
relaxationFactor = _relaxationFactor;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
motorVelocity = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments,
|
||||
"motorVelocity", ok, false);
|
||||
if (!ok) {
|
||||
motorVelocity = _motorVelocity;
|
||||
}
|
||||
|
||||
if (somethingChanged ||
|
||||
pivotInA != _pivotInA ||
|
||||
axisInA != _axisInA ||
|
||||
otherEntityID != _otherEntityID ||
|
||||
pivotInB != _pivotInB ||
|
||||
axisInB != _axisInB ||
|
||||
low != _low ||
|
||||
high != _high ||
|
||||
softness != _softness ||
|
||||
biasFactor != _biasFactor ||
|
||||
relaxationFactor != _relaxationFactor ||
|
||||
motorVelocity != _motorVelocity) {
|
||||
// something changed
|
||||
needUpdate = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (needUpdate) {
|
||||
withWriteLock([&] {
|
||||
_pivotInA = pivotInA;
|
||||
_axisInA = axisInA;
|
||||
_otherEntityID = otherEntityID;
|
||||
_pivotInB = pivotInB;
|
||||
_axisInB = axisInB;
|
||||
_low = low;
|
||||
_high = high;
|
||||
_softness = softness;
|
||||
_biasFactor = biasFactor;
|
||||
_relaxationFactor = relaxationFactor;
|
||||
_motorVelocity = motorVelocity;
|
||||
|
||||
_active = true;
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
|
||||
updateHinge();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantMap ObjectConstraintHinge::getArguments() {
|
||||
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||
withReadLock([&] {
|
||||
if (_constraint) {
|
||||
arguments["pivot"] = glmToQMap(_pivotInA);
|
||||
arguments["axis"] = glmToQMap(_axisInA);
|
||||
arguments["otherEntityID"] = _otherEntityID;
|
||||
arguments["otherPivot"] = glmToQMap(_pivotInB);
|
||||
arguments["otherAxis"] = glmToQMap(_axisInB);
|
||||
arguments["low"] = _low;
|
||||
arguments["high"] = _high;
|
||||
arguments["softness"] = _softness;
|
||||
arguments["biasFactor"] = _biasFactor;
|
||||
arguments["relaxationFactor"] = _relaxationFactor;
|
||||
arguments["motorVelocity"] = _motorVelocity;
|
||||
arguments["angle"] = static_cast<btHingeConstraint*>(_constraint)->getHingeAngle(); // [-PI,PI]
|
||||
}
|
||||
});
|
||||
return arguments;
|
||||
}
|
||||
|
||||
QByteArray ObjectConstraintHinge::serialize() const {
|
||||
QByteArray serializedConstraintArguments;
|
||||
QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly);
|
||||
|
||||
dataStream << DYNAMIC_TYPE_HINGE;
|
||||
dataStream << getID();
|
||||
dataStream << ObjectConstraintHinge::constraintVersion;
|
||||
|
||||
withReadLock([&] {
|
||||
dataStream << _pivotInA;
|
||||
dataStream << _axisInA;
|
||||
dataStream << _otherEntityID;
|
||||
dataStream << _pivotInB;
|
||||
dataStream << _axisInB;
|
||||
dataStream << _low;
|
||||
dataStream << _high;
|
||||
dataStream << _softness;
|
||||
dataStream << _biasFactor;
|
||||
dataStream << _relaxationFactor;
|
||||
|
||||
dataStream << localTimeToServerTime(_expires);
|
||||
dataStream << _tag;
|
||||
|
||||
dataStream << _motorVelocity;
|
||||
});
|
||||
|
||||
return serializedConstraintArguments;
|
||||
}
|
||||
|
||||
void ObjectConstraintHinge::deserialize(QByteArray serializedArguments) {
|
||||
QDataStream dataStream(serializedArguments);
|
||||
|
||||
EntityDynamicType type;
|
||||
dataStream >> type;
|
||||
assert(type == getType());
|
||||
|
||||
QUuid id;
|
||||
dataStream >> id;
|
||||
assert(id == getID());
|
||||
|
||||
uint16_t serializationVersion;
|
||||
dataStream >> serializationVersion;
|
||||
if (serializationVersion != ObjectConstraintHinge::constraintVersion) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
dataStream >> _pivotInA;
|
||||
dataStream >> _axisInA;
|
||||
dataStream >> _otherEntityID;
|
||||
dataStream >> _pivotInB;
|
||||
dataStream >> _axisInB;
|
||||
dataStream >> _low;
|
||||
dataStream >> _high;
|
||||
dataStream >> _softness;
|
||||
dataStream >> _biasFactor;
|
||||
dataStream >> _relaxationFactor;
|
||||
|
||||
quint64 serverExpires;
|
||||
dataStream >> serverExpires;
|
||||
_expires = serverTimeToLocalTime(serverExpires);
|
||||
|
||||
dataStream >> _tag;
|
||||
|
||||
dataStream >> _motorVelocity;
|
||||
|
||||
_active = true;
|
||||
});
|
||||
}
|
53
libraries/physics/src/ObjectConstraintHinge.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// ObjectConstraintHinge.h
|
||||
// libraries/physics/src
|
||||
//
|
||||
// Created by Seth Alves 2017-4-11
|
||||
// Copyright 2017 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_ObjectConstraintHinge_h
|
||||
#define hifi_ObjectConstraintHinge_h
|
||||
|
||||
#include "ObjectConstraint.h"
|
||||
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtHingeConstraint.html
|
||||
|
||||
class ObjectConstraintHinge : public ObjectConstraint {
|
||||
public:
|
||||
ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectConstraintHinge();
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
|
||||
virtual QByteArray serialize() const override;
|
||||
virtual void deserialize(QByteArray serializedArguments) override;
|
||||
|
||||
virtual QList<btRigidBody*> getRigidBodies() override;
|
||||
virtual btTypedConstraint* getConstraint() override;
|
||||
|
||||
protected:
|
||||
static const uint16_t constraintVersion;
|
||||
|
||||
void updateHinge();
|
||||
|
||||
glm::vec3 _pivotInA;
|
||||
glm::vec3 _axisInA;
|
||||
|
||||
EntityItemID _otherEntityID;
|
||||
glm::vec3 _pivotInB;
|
||||
glm::vec3 _axisInB;
|
||||
|
||||
float _low { -2.0f * PI };
|
||||
float _high { 2.0f * PI };
|
||||
float _softness { 0.9f };
|
||||
float _biasFactor { 0.3f };
|
||||
float _relaxationFactor { 1.0f };
|
||||
float _motorVelocity { 0.0f };
|
||||
};
|
||||
|
||||
#endif // hifi_ObjectConstraintHinge_h
|
276
libraries/physics/src/ObjectDynamic.cpp
Normal file
|
@ -0,0 +1,276 @@
|
|||
//
|
||||
// ObjectDynamic.cpp
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "ObjectDynamic.h"
|
||||
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
|
||||
ObjectDynamic::ObjectDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
|
||||
EntityDynamicInterface(type, id),
|
||||
_ownerEntity(ownerEntity) {
|
||||
}
|
||||
|
||||
ObjectDynamic::~ObjectDynamic() {
|
||||
}
|
||||
|
||||
qint64 ObjectDynamic::getEntityServerClockSkew() const {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QUuid& entityServerNodeID = ownerEntity->getSourceUUID();
|
||||
auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID);
|
||||
if (entityServerNode) {
|
||||
return entityServerNode->getClockSkewUsec();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ObjectDynamic::updateArguments(QVariantMap arguments) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
withWriteLock([&]{
|
||||
quint64 previousExpires = _expires;
|
||||
QString previousTag = _tag;
|
||||
|
||||
bool ttlSet = true;
|
||||
float ttl = EntityDynamicInterface::extractFloatArgument("dynamic", arguments, "ttl", ttlSet, false);
|
||||
if (ttlSet) {
|
||||
quint64 now = usecTimestampNow();
|
||||
_expires = now + (quint64)(ttl * USECS_PER_SECOND);
|
||||
} else {
|
||||
_expires = 0;
|
||||
}
|
||||
|
||||
bool tagSet = true;
|
||||
QString tag = EntityDynamicInterface::extractStringArgument("dynamic", arguments, "tag", tagSet, false);
|
||||
if (tagSet) {
|
||||
_tag = tag;
|
||||
} else {
|
||||
tag = "";
|
||||
}
|
||||
|
||||
if (previousExpires != _expires || previousTag != _tag) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
QVariantMap ObjectDynamic::getArguments() {
|
||||
QVariantMap arguments;
|
||||
withReadLock([&]{
|
||||
if (_expires == 0) {
|
||||
arguments["ttl"] = 0.0f;
|
||||
} else {
|
||||
quint64 now = usecTimestampNow();
|
||||
arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND;
|
||||
}
|
||||
arguments["tag"] = _tag;
|
||||
|
||||
EntityItemPointer entity = _ownerEntity.lock();
|
||||
if (entity) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
arguments["::active"] = motionState->isActive();
|
||||
arguments["::motion-type"] = motionTypeToString(motionState->getMotionType());
|
||||
} else {
|
||||
arguments["::no-motion-state"] = true;
|
||||
}
|
||||
}
|
||||
arguments["isMine"] = isMine();
|
||||
});
|
||||
return arguments;
|
||||
}
|
||||
|
||||
void ObjectDynamic::removeFromSimulation(EntitySimulationPointer simulation) const {
|
||||
QUuid myID;
|
||||
withReadLock([&]{
|
||||
myID = _id;
|
||||
});
|
||||
simulation->removeDynamic(myID);
|
||||
}
|
||||
|
||||
EntityItemPointer ObjectDynamic::getEntityByID(EntityItemID entityID) const {
|
||||
EntityItemPointer ownerEntity;
|
||||
withReadLock([&]{
|
||||
ownerEntity = _ownerEntity.lock();
|
||||
});
|
||||
EntityTreeElementPointer element = ownerEntity ? ownerEntity->getElement() : nullptr;
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
if (!tree) {
|
||||
return nullptr;
|
||||
}
|
||||
return tree->findEntityByID(entityID);
|
||||
}
|
||||
|
||||
|
||||
btRigidBody* ObjectDynamic::getRigidBody() {
|
||||
ObjectMotionState* motionState = nullptr;
|
||||
withReadLock([&]{
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
if (!physicsInfo) {
|
||||
return;
|
||||
}
|
||||
motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
});
|
||||
if (motionState) {
|
||||
return motionState->getRigidBody();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glm::vec3 ObjectDynamic::getPosition() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getCenterOfMassPosition());
|
||||
}
|
||||
|
||||
glm::quat ObjectDynamic::getRotation() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getOrientation());
|
||||
}
|
||||
|
||||
glm::vec3 ObjectDynamic::getLinearVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getLinearVelocity());
|
||||
}
|
||||
|
||||
void ObjectDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
glm::vec3 ObjectDynamic::getAngularVelocity() {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
return bulletToGLM(rigidBody->getAngularVelocity());
|
||||
}
|
||||
|
||||
void ObjectDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (!rigidBody) {
|
||||
return;
|
||||
}
|
||||
rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
|
||||
rigidBody->activate();
|
||||
}
|
||||
|
||||
void ObjectDynamic::activateBody(bool forceActivation) {
|
||||
auto rigidBody = getRigidBody();
|
||||
if (rigidBody) {
|
||||
rigidBody->activate(forceActivation);
|
||||
} else {
|
||||
qCDebug(physics) << "ObjectDynamic::activateBody -- no rigid body" << (void*)rigidBody;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDynamic::forceBodyNonStatic() {
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (!ownerEntity) {
|
||||
return;
|
||||
}
|
||||
void* physicsInfo = ownerEntity->getPhysicsInfo();
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||
if (motionState && motionState->getMotionType() == MOTION_TYPE_STATIC) {
|
||||
ownerEntity->flagForMotionStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectDynamic::lifetimeIsOver() {
|
||||
if (_expires == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (now >= _expires) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
quint64 ObjectDynamic::localTimeToServerTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew < 0 && timeValue <= (quint64)(-serverClockSkew)) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue + serverClockSkew;
|
||||
}
|
||||
|
||||
quint64 ObjectDynamic::serverTimeToLocalTime(quint64 timeValue) const {
|
||||
// 0 indicates no expiration
|
||||
if (timeValue == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 serverClockSkew = getEntityServerClockSkew();
|
||||
if (serverClockSkew > 0 && timeValue <= (quint64)serverClockSkew) {
|
||||
return 1; // non-zero but long-expired value to avoid negative roll-over
|
||||
}
|
||||
|
||||
return timeValue - serverClockSkew;
|
||||
}
|
||||
|
||||
btRigidBody* ObjectDynamic::getOtherRigidBody(EntityItemID otherEntityID) {
|
||||
EntityItemPointer otherEntity = getEntityByID(otherEntityID);
|
||||
if (!otherEntity) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* otherPhysicsInfo = otherEntity->getPhysicsInfo();
|
||||
if (!otherPhysicsInfo) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectMotionState* otherMotionState = static_cast<ObjectMotionState*>(otherPhysicsInfo);
|
||||
if (!otherMotionState) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return otherMotionState->getRigidBody();
|
||||
}
|
||||
|
||||
QList<btRigidBody*> ObjectDynamic::getRigidBodies() {
|
||||
QList<btRigidBody*> result;
|
||||
result += getRigidBody();
|
||||
return result;
|
||||
}
|
76
libraries/physics/src/ObjectDynamic.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// ObjectDynamic.h
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Seth Alves 2015-6-2
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html
|
||||
|
||||
#ifndef hifi_ObjectDynamic_h
|
||||
#define hifi_ObjectDynamic_h
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
|
||||
class ObjectDynamic : public EntityDynamicInterface, public ReadWriteLockable {
|
||||
public:
|
||||
ObjectDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||
virtual ~ObjectDynamic();
|
||||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; }
|
||||
|
||||
virtual void invalidate() {};
|
||||
|
||||
virtual bool updateArguments(QVariantMap arguments) override;
|
||||
virtual QVariantMap getArguments() override;
|
||||
|
||||
|
||||
virtual QByteArray serialize() const override = 0;
|
||||
virtual void deserialize(QByteArray serializedArguments) override = 0;
|
||||
|
||||
virtual bool lifetimeIsOver() override;
|
||||
virtual quint64 getExpires() override { return _expires; }
|
||||
|
||||
virtual QList<btRigidBody*> getRigidBodies();
|
||||
|
||||
protected:
|
||||
quint64 localTimeToServerTime(quint64 timeValue) const;
|
||||
quint64 serverTimeToLocalTime(quint64 timeValue) const;
|
||||
|
||||
btRigidBody* getOtherRigidBody(EntityItemID otherEntityID);
|
||||
EntityItemPointer getEntityByID(EntityItemID entityID) const;
|
||||
virtual btRigidBody* getRigidBody();
|
||||
virtual glm::vec3 getPosition() override;
|
||||
virtual glm::quat getRotation() override;
|
||||
virtual glm::vec3 getLinearVelocity() override;
|
||||
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||
virtual glm::vec3 getAngularVelocity() override;
|
||||
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
|
||||
virtual void activateBody(bool forceActivation = false);
|
||||
virtual void forceBodyNonStatic();
|
||||
|
||||
EntityItemWeakPointer _ownerEntity;
|
||||
QString _tag;
|
||||
quint64 _expires { 0 }; // in seconds since epoch
|
||||
|
||||
private:
|
||||
qint64 getEntityServerClockSkew() const;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ObjectDynamic> ObjectDynamicPointer;
|
||||
|
||||
#endif // hifi_ObjectDynamic_h
|
|
@ -330,33 +330,41 @@ void PhysicalEntitySimulation::handleCollisionEvents(const CollisionEvents& coll
|
|||
}
|
||||
|
||||
|
||||
void PhysicalEntitySimulation::addAction(EntityActionPointer action) {
|
||||
void PhysicalEntitySimulation::addDynamic(EntityDynamicPointer dynamic) {
|
||||
if (_physicsEngine) {
|
||||
// FIXME put fine grain locking into _physicsEngine
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
const QUuid& actionID = action->getID();
|
||||
if (_physicsEngine->getActionByID(actionID)) {
|
||||
qCDebug(physics) << "warning -- PhysicalEntitySimulation::addAction -- adding an "
|
||||
"action that was already in _physicsEngine";
|
||||
const QUuid& dynamicID = dynamic->getID();
|
||||
if (_physicsEngine->getDynamicByID(dynamicID)) {
|
||||
qCDebug(physics) << "warning -- PhysicalEntitySimulation::addDynamic -- adding an "
|
||||
"dynamic that was already in _physicsEngine";
|
||||
}
|
||||
}
|
||||
EntitySimulation::addAction(action);
|
||||
EntitySimulation::addDynamic(dynamic);
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::applyActionChanges() {
|
||||
void PhysicalEntitySimulation::applyDynamicChanges() {
|
||||
QList<EntityDynamicPointer> dynamicsFailedToAdd;
|
||||
if (_physicsEngine) {
|
||||
// FIXME put fine grain locking into _physicsEngine
|
||||
QMutexLocker lock(&_mutex);
|
||||
foreach(QUuid actionToRemove, _actionsToRemove) {
|
||||
_physicsEngine->removeAction(actionToRemove);
|
||||
foreach(QUuid dynamicToRemove, _dynamicsToRemove) {
|
||||
_physicsEngine->removeDynamic(dynamicToRemove);
|
||||
}
|
||||
foreach (EntityActionPointer actionToAdd, _actionsToAdd) {
|
||||
if (!_actionsToRemove.contains(actionToAdd->getID())) {
|
||||
_physicsEngine->addAction(actionToAdd);
|
||||
foreach (EntityDynamicPointer dynamicToAdd, _dynamicsToAdd) {
|
||||
if (!_dynamicsToRemove.contains(dynamicToAdd->getID())) {
|
||||
if (!_physicsEngine->addDynamic(dynamicToAdd)) {
|
||||
dynamicsFailedToAdd += dynamicToAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EntitySimulation::applyActionChanges();
|
||||
// applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd
|
||||
EntitySimulation::applyDynamicChanges();
|
||||
// put back the ones that couldn't yet be added
|
||||
foreach (EntityDynamicPointer dynamicFailedToAdd, dynamicsFailedToAdd) {
|
||||
addDynamic(dynamicFailedToAdd);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ public:
|
|||
|
||||
void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender);
|
||||
|
||||
virtual void addAction(EntityActionPointer action) override;
|
||||
virtual void applyActionChanges() override;
|
||||
virtual void addDynamic(EntityDynamicPointer dynamic) override;
|
||||
virtual void applyDynamicChanges() override;
|
||||
|
||||
virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) override;
|
||||
|
||||
|
|
|
@ -142,6 +142,26 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
motionState->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
QList<EntityDynamicPointer> PhysicsEngine::removeDynamicsForBody(btRigidBody* body) {
|
||||
// remove dynamics that are attached to this body
|
||||
QList<EntityDynamicPointer> removedDynamics;
|
||||
QMutableSetIterator<QUuid> i(_objectDynamicsByBody[body]);
|
||||
|
||||
while (i.hasNext()) {
|
||||
QUuid dynamicID = i.next();
|
||||
if (dynamicID.isNull()) {
|
||||
continue;
|
||||
}
|
||||
EntityDynamicPointer dynamic = _objectDynamics[dynamicID];
|
||||
if (!dynamic) {
|
||||
continue;
|
||||
}
|
||||
removeDynamic(dynamicID);
|
||||
removedDynamics += dynamic;
|
||||
}
|
||||
return removedDynamics;
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
||||
// bump and prune contacts for all objects in the list
|
||||
for (auto object : objects) {
|
||||
|
@ -175,6 +195,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
|||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
if (body) {
|
||||
removeDynamicsForBody(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
|
@ -191,6 +212,7 @@ void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
|
|||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
if (body) {
|
||||
removeDynamicsForBody(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
|
@ -240,7 +262,6 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
|||
btRigidBody* body = object->getRigidBody();
|
||||
if (body) {
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// add it back
|
||||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
|
@ -548,44 +569,84 @@ void PhysicsEngine::setCharacterController(CharacterController* character) {
|
|||
}
|
||||
}
|
||||
|
||||
EntityActionPointer PhysicsEngine::getActionByID(const QUuid& actionID) const {
|
||||
if (_objectActions.contains(actionID)) {
|
||||
return _objectActions[actionID];
|
||||
EntityDynamicPointer PhysicsEngine::getDynamicByID(const QUuid& dynamicID) const {
|
||||
if (_objectDynamics.contains(dynamicID)) {
|
||||
return _objectDynamics[dynamicID];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PhysicsEngine::addAction(EntityActionPointer action) {
|
||||
assert(action);
|
||||
const QUuid& actionID = action->getID();
|
||||
if (_objectActions.contains(actionID)) {
|
||||
if (_objectActions[actionID] == action) {
|
||||
bool PhysicsEngine::addDynamic(EntityDynamicPointer dynamic) {
|
||||
assert(dynamic);
|
||||
|
||||
if (!dynamic->isReadyForAdd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QUuid& dynamicID = dynamic->getID();
|
||||
if (_objectDynamics.contains(dynamicID)) {
|
||||
if (_objectDynamics[dynamicID] == dynamic) {
|
||||
return true;
|
||||
}
|
||||
removeDynamic(dynamic->getID());
|
||||
}
|
||||
|
||||
bool success { false };
|
||||
if (dynamic->isAction()) {
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(dynamic.get());
|
||||
_dynamicsWorld->addAction(objectAction);
|
||||
success = true;
|
||||
} else if (dynamic->isConstraint()) {
|
||||
ObjectConstraint* objectConstraint = static_cast<ObjectConstraint*>(dynamic.get());
|
||||
btTypedConstraint* constraint = objectConstraint->getConstraint();
|
||||
if (constraint) {
|
||||
_dynamicsWorld->addConstraint(constraint);
|
||||
success = true;
|
||||
} // else perhaps not all the rigid bodies are available, yet
|
||||
}
|
||||
|
||||
if (success) {
|
||||
_objectDynamics[dynamicID] = dynamic;
|
||||
foreach(btRigidBody* rigidBody, std::static_pointer_cast<ObjectDynamic>(dynamic)->getRigidBodies()) {
|
||||
_objectDynamicsByBody[rigidBody] += dynamic->getID();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeDynamic(const QUuid dynamicID) {
|
||||
if (_objectDynamics.contains(dynamicID)) {
|
||||
ObjectDynamicPointer dynamic = std::static_pointer_cast<ObjectDynamic>(_objectDynamics[dynamicID]);
|
||||
if (!dynamic) {
|
||||
return;
|
||||
}
|
||||
removeAction(action->getID());
|
||||
}
|
||||
|
||||
_objectActions[actionID] = action;
|
||||
|
||||
// bullet needs a pointer to the action, but it doesn't use shared pointers.
|
||||
// is there a way to bump the reference count?
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(action.get());
|
||||
_dynamicsWorld->addAction(objectAction);
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeAction(const QUuid actionID) {
|
||||
if (_objectActions.contains(actionID)) {
|
||||
EntityActionPointer action = _objectActions[actionID];
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(action.get());
|
||||
_dynamicsWorld->removeAction(objectAction);
|
||||
_objectActions.remove(actionID);
|
||||
QList<btRigidBody*> rigidBodies = dynamic->getRigidBodies();
|
||||
if (dynamic->isAction()) {
|
||||
ObjectAction* objectAction = static_cast<ObjectAction*>(dynamic.get());
|
||||
_dynamicsWorld->removeAction(objectAction);
|
||||
} else {
|
||||
ObjectConstraint* objectConstraint = static_cast<ObjectConstraint*>(dynamic.get());
|
||||
btTypedConstraint* constraint = objectConstraint->getConstraint();
|
||||
if (constraint) {
|
||||
_dynamicsWorld->removeConstraint(constraint);
|
||||
} else {
|
||||
qCDebug(physics) << "PhysicsEngine::removeDynamic of constraint failed";
|
||||
}
|
||||
}
|
||||
_objectDynamics.remove(dynamicID);
|
||||
foreach(btRigidBody* rigidBody, rigidBodies) {
|
||||
_objectDynamicsByBody[rigidBody].remove(dynamic->getID());
|
||||
}
|
||||
dynamic->invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::forEachAction(std::function<void(EntityActionPointer)> actor) {
|
||||
QHashIterator<QUuid, EntityActionPointer> iter(_objectActions);
|
||||
void PhysicsEngine::forEachDynamic(std::function<void(EntityDynamicPointer)> actor) {
|
||||
QMutableHashIterator<QUuid, EntityDynamicPointer> iter(_objectDynamics);
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
actor(iter.value());
|
||||
if (iter.value()) {
|
||||
actor(iter.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "ObjectMotionState.h"
|
||||
#include "ThreadSafeDynamicsWorld.h"
|
||||
#include "ObjectAction.h"
|
||||
#include "ObjectConstraint.h"
|
||||
|
||||
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
|
||||
|
||||
|
@ -84,12 +85,13 @@ public:
|
|||
|
||||
void dumpNextStats() { _dumpNextStats = true; }
|
||||
|
||||
EntityActionPointer getActionByID(const QUuid& actionID) const;
|
||||
void addAction(EntityActionPointer action);
|
||||
void removeAction(const QUuid actionID);
|
||||
void forEachAction(std::function<void(EntityActionPointer)> actor);
|
||||
EntityDynamicPointer getDynamicByID(const QUuid& dynamicID) const;
|
||||
bool addDynamic(EntityDynamicPointer dynamic);
|
||||
void removeDynamic(const QUuid dynamicID);
|
||||
void forEachDynamic(std::function<void(EntityDynamicPointer)> actor);
|
||||
|
||||
private:
|
||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||
void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName);
|
||||
|
||||
|
@ -110,7 +112,8 @@ private:
|
|||
|
||||
ContactMap _contactMap;
|
||||
CollisionEvents _collisionEvents;
|
||||
QHash<QUuid, EntityActionPointer> _objectActions;
|
||||
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
||||
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
||||
std::vector<btRigidBody*> _activeStaticBodies;
|
||||
|
||||
glm::vec3 _originOffset;
|
||||
|
@ -122,6 +125,7 @@ private:
|
|||
|
||||
bool _dumpNextStats = false;
|
||||
bool _hasOutgoingChanges = false;
|
||||
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<PhysicsEngine> PhysicsEnginePointer;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
set(TARGET_NAME hifiCodec)
|
||||
setup_hifi_client_server_plugin()
|
||||
link_hifi_libraries(audio shared plugins)
|
||||
link_hifi_libraries(audio plugins)
|
||||
add_dependency_external_projects(hifiAudioCodec)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES})
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include <AudioCodec.h>
|
||||
#include <AudioConstants.h>
|
||||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "HiFiCodec.h"
|
||||
|
||||
const char* HiFiCodec::NAME { "hifiAC" };
|
||||
|
|
|
@ -529,7 +529,7 @@ Grabber.prototype.moveEvent = function(event) {
|
|||
|
||||
if (!this.actionID) {
|
||||
if (!entityIsGrabbedByOther(this.entityID)) {
|
||||
this.actionID = Entities.addAction("spring", this.entityID, actionArgs);
|
||||
this.actionID = Entities.addAction("far-grab", this.entityID, actionArgs);
|
||||
}
|
||||
} else {
|
||||
Entities.updateAction(this.entityID, this.actionID, actionArgs);
|
||||
|
|
|
@ -2526,7 +2526,7 @@ function MyController(hand) {
|
|||
var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject);
|
||||
this.linearTimeScale = timeScale;
|
||||
this.actionID = NULL_UUID;
|
||||
this.actionID = Entities.addAction("spring", this.grabbedThingID, {
|
||||
this.actionID = Entities.addAction("far-grab", this.grabbedThingID, {
|
||||
targetPosition: this.currentObjectPosition,
|
||||
linearTimeScale: timeScale,
|
||||
targetRotation: this.currentObjectRotation,
|
||||
|
@ -2741,7 +2741,7 @@ function MyController(hand) {
|
|||
relativePosition: this.offsetPosition,
|
||||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematic: this.kinematicGrab,
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
|
@ -2838,12 +2838,14 @@ function MyController(hand) {
|
|||
this.ignoreIK = true;
|
||||
} else {
|
||||
grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES);
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedThingID, DEFAULT_GRABBABLE_DATA);
|
||||
if (FORCE_IGNORE_IK) {
|
||||
this.ignoreIK = true;
|
||||
} else {
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedThingID, DEFAULT_GRABBABLE_DATA);
|
||||
this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true;
|
||||
}
|
||||
|
||||
this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC;
|
||||
}
|
||||
|
||||
var handRotation;
|
||||
|
@ -3207,7 +3209,7 @@ function MyController(hand) {
|
|||
relativePosition: this.offsetPosition,
|
||||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematic: this.kinematicGrab,
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
|
|
|
@ -289,7 +289,7 @@ input[type=number]::-webkit-inner-spin-button:after {
|
|||
bottom: 4px;
|
||||
}
|
||||
|
||||
input[type=number].hover-up::-webkit-inner-spin-button:before,
|
||||
input[type=number].hover-up::-webkit-inner-spin-button:before,
|
||||
input[type=number].hover-down::-webkit-inner-spin-button:after {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
@ -1037,6 +1037,12 @@ th#entity-hasTransparent .sort-order {
|
|||
top: -1px;
|
||||
}
|
||||
|
||||
#entity-table td.isBaked.glyph {
|
||||
font-size: 22px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
#entity-table tfoot {
|
||||
box-sizing: border-box;
|
||||
border: 2px solid #575757;
|
||||
|
@ -1062,7 +1068,7 @@ th#entity-hasTransparent .sort-order {
|
|||
#col-locked, #col-visible {
|
||||
width: 9%;
|
||||
}
|
||||
#col-verticesCount, #col-texturesCount, #col-texturesSize, #col-hasTransparent, #col-drawCalls, #col-hasScript {
|
||||
#col-verticesCount, #col-texturesCount, #col-texturesSize, #col-hasTransparent, #col-isBaked, #col-drawCalls, #col-hasScript {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
|
@ -1090,6 +1096,9 @@ th#entity-hasTransparent .sort-order {
|
|||
.showExtraInfo #col-hasTransparent {
|
||||
width: 4%;
|
||||
}
|
||||
.showExtraInfo #col-isBaked {
|
||||
width: 8%;
|
||||
}
|
||||
.showExtraInfo #col-drawCalls {
|
||||
width: 8%;
|
||||
}
|
||||
|
@ -1097,12 +1106,12 @@ th#entity-hasTransparent .sort-order {
|
|||
width: 6%;
|
||||
}
|
||||
|
||||
th#entity-verticesCount, th#entity-texturesCount, th#entity-texturesSize, th#entity-hasTransparent, th#entity-drawCalls,
|
||||
th#entity-verticesCount, th#entity-texturesCount, th#entity-texturesSize, th#entity-hasTransparent, th#entity-isBaked, th#entity-drawCalls,
|
||||
th#entity-hasScript {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.verticesCount, .texturesCount, .texturesSize, .hasTransparent, .drawCalls, .hasScript {
|
||||
.verticesCount, .texturesCount, .texturesSize, .hasTransparent, .isBaked, .drawCalls, .hasScript {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -1110,13 +1119,13 @@ th#entity-hasScript {
|
|||
border: none;
|
||||
}
|
||||
|
||||
.showExtraInfo #entity-verticesCount, .showExtraInfo #entity-texturesCount, .showExtraInfo #entity-texturesSize,
|
||||
.showExtraInfo #entity-hasTransparent, .showExtraInfo #entity-drawCalls, .showExtraInfo #entity-hasScript {
|
||||
.showExtraInfo #entity-verticesCount, .showExtraInfo #entity-texturesCount, .showExtraInfo #entity-texturesSize,
|
||||
.showExtraInfo #entity-hasTransparent, .showExtraInfo #entity-isBaked, .showExtraInfo #entity-drawCalls, .showExtraInfo #entity-hasScript {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.showExtraInfo .verticesCount, .showExtraInfo .texturesCount, .showExtraInfo .texturesSize, .showExtraInfo .hasTransparent,
|
||||
.showExtraInfo .drawCalls, .showExtraInfo .hasScript {
|
||||
.showExtraInfo .verticesCount, .showExtraInfo .texturesCount, .showExtraInfo .texturesSize, .showExtraInfo .hasTransparent,
|
||||
.showExtraInfo .isBaked, .showExtraInfo .drawCalls, .showExtraInfo .hasScript {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<col span="1" id="col-texturesCount" />
|
||||
<col span="1" id="col-texturesSize" />
|
||||
<col span="1" id="col-hasTransparent" />
|
||||
<col span="1" id="col-isBaked" />
|
||||
<col span="1" id="col-drawCalls" />
|
||||
<col span="1" id="col-hasScript" />
|
||||
</colgroup>
|
||||
|
@ -63,6 +64,7 @@
|
|||
<th id="entity-texturesCount">Texts<span class="sort-order"></span></th>
|
||||
<th id="entity-texturesSize">Text MB<span class="sort-order"></span></th>
|
||||
<th id="entity-hasTransparent"><span class="glyph"></span><span class="sort-order"></span></th>
|
||||
<th id="entity-isBaked">Baked<span class="sort-order"></span></th>
|
||||
<th id="entity-drawCalls">Draws<span class="sort-order"></span></th>
|
||||
<th colspan="1" id="entity-hasScript"><span class="glyph">k</span><span class="sort-order"></span></th>
|
||||
</tr>
|
||||
|
@ -78,6 +80,7 @@
|
|||
<td class="texturesCount"></td>
|
||||
<td class="texturesSize"></td>
|
||||
<td class="hasTransparent glyph"></td>
|
||||
<td class="isBaked glyph"></td>
|
||||
<td class="drawCalls"></td>
|
||||
<td class="hasScript glyph"></td>
|
||||
<td class="id" style="display: none"></td>
|
||||
|
@ -85,7 +88,7 @@
|
|||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td id="footer-text" colspan="11"> </td>
|
||||
<td id="footer-text" colspan="12"> </td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
|
@ -17,6 +17,7 @@ const DESCENDING_STRING = '▾';
|
|||
const LOCKED_GLYPH = "";
|
||||
const VISIBLE_GLYPH = "";
|
||||
const TRANSPARENCY_GLYPH = "";
|
||||
const BAKED_GLYPH = ""
|
||||
const SCRIPT_GLYPH = "k";
|
||||
const DELETE = 46; // Key code for the delete key.
|
||||
const KEY_P = 80; // Key code for letter p used for Parenting hotkey.
|
||||
|
@ -77,6 +78,9 @@ function loaded() {
|
|||
document.getElementById("entity-hasTransparent").onclick = function () {
|
||||
setSortColumn('hasTransparent');
|
||||
};
|
||||
document.getElementById("entity-isBaked").onclick = function () {
|
||||
setSortColumn('isBaked');
|
||||
};
|
||||
document.getElementById("entity-drawCalls").onclick = function () {
|
||||
setSortColumn('drawCalls');
|
||||
};
|
||||
|
@ -147,7 +151,7 @@ function loaded() {
|
|||
}
|
||||
|
||||
function addEntity(id, name, type, url, locked, visible, verticesCount, texturesCount, texturesSize, hasTransparent,
|
||||
drawCalls, hasScript) {
|
||||
isBaked, drawCalls, hasScript) {
|
||||
|
||||
var urlParts = url.split('/');
|
||||
var filename = urlParts[urlParts.length - 1];
|
||||
|
@ -157,7 +161,7 @@ function loaded() {
|
|||
id: id, name: name, type: type, url: filename, locked: locked, visible: visible,
|
||||
verticesCount: displayIfNonZero(verticesCount), texturesCount: displayIfNonZero(texturesCount),
|
||||
texturesSize: decimalMegabytes(texturesSize), hasTransparent: hasTransparent,
|
||||
drawCalls: displayIfNonZero(drawCalls), hasScript: hasScript
|
||||
isBaked: isBaked, drawCalls: displayIfNonZero(drawCalls), hasScript: hasScript
|
||||
}],
|
||||
function (items) {
|
||||
var currentElement = items[0].elm;
|
||||
|
@ -201,6 +205,7 @@ function loaded() {
|
|||
texturesCount: document.querySelector('#entity-texturesCount .sort-order'),
|
||||
texturesSize: document.querySelector('#entity-texturesSize .sort-order'),
|
||||
hasTransparent: document.querySelector('#entity-hasTransparent .sort-order'),
|
||||
isBaked: document.querySelector('#entity-isBaked .sort-order'),
|
||||
drawCalls: document.querySelector('#entity-drawCalls .sort-order'),
|
||||
hasScript: document.querySelector('#entity-hasScript .sort-order'),
|
||||
}
|
||||
|
@ -350,6 +355,7 @@ function loaded() {
|
|||
newEntities[i].visible ? VISIBLE_GLYPH : null,
|
||||
newEntities[i].verticesCount, newEntities[i].texturesCount, newEntities[i].texturesSize,
|
||||
newEntities[i].hasTransparent ? TRANSPARENCY_GLYPH : null,
|
||||
newEntities[i].isBaked ? BAKED_GLYPH : null,
|
||||
newEntities[i].drawCalls,
|
||||
newEntities[i].hasScript ? SCRIPT_GLYPH : null);
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ EntityListTool = function(opts) {
|
|||
texturesCount: valueIfDefined(properties.renderInfo.texturesCount),
|
||||
texturesSize: valueIfDefined(properties.renderInfo.texturesSize),
|
||||
hasTransparent: valueIfDefined(properties.renderInfo.hasTransparent),
|
||||
isBaked: properties.type == "Model" ? properties.modelURL.toLowerCase().endsWith(".baked.fbx") : false,
|
||||
drawCalls: valueIfDefined(properties.renderInfo.drawCalls),
|
||||
hasScript: properties.script !== ""
|
||||
});
|
||||
|
|
153
scripts/tutorials/createTetherballStick.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
"use strict";
|
||||
/* jslint vars: true, plusplus: true, forin: true*/
|
||||
/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */
|
||||
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||
//
|
||||
// createTetherballStick.js
|
||||
//
|
||||
// Created by Triplelexx on 17/03/04
|
||||
// Updated by MrRoboman on 17/03/26
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Creates an equippable stick with a tethered ball
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
var NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
|
||||
var LIFETIME = 3600;
|
||||
var BALL_SIZE = 0.175;
|
||||
var BALL_DAMPING = 0.5;
|
||||
var BALL_ANGULAR_DAMPING = 0.5;
|
||||
var BALL_RESTITUTION = 0.4;
|
||||
var BALL_DENSITY = 1000;
|
||||
var ACTION_DISTANCE = 0.35;
|
||||
var ACTION_TIMESCALE = 0.035;
|
||||
var MAX_DISTANCE_MULTIPLIER = 4;
|
||||
var STICK_SCRIPT_URL = Script.resolvePath("./entity_scripts/tetherballStick.js?v=" + Date.now());
|
||||
var STICK_MODEL_URL = "http://hifi-content.s3.amazonaws.com/caitlyn/production/raveStick/newRaveStick2.fbx";
|
||||
var COLLISION_SOUND_URL = "http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav";
|
||||
|
||||
var avatarOrientation = MyAvatar.orientation;
|
||||
avatarOrientation = Quat.safeEulerAngles(avatarOrientation);
|
||||
avatarOrientation.x = 0;
|
||||
avatarOrientation = Quat.fromVec3Degrees(avatarOrientation);
|
||||
var front = Quat.getFront(avatarOrientation);
|
||||
var stickStartPosition = Vec3.sum(MyAvatar.getRightPalmPosition(), front);
|
||||
var ballStartPosition = Vec3.sum(stickStartPosition, Vec3.multiply(0.36, front));
|
||||
|
||||
var ballID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: "http://hifi-content.s3.amazonaws.com/Examples%20Content/production/marblecollection/Star.fbx",
|
||||
name: "TetherballStick Ball",
|
||||
shapeType: "Sphere",
|
||||
position: ballStartPosition,
|
||||
lifetime: LIFETIME,
|
||||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
dimensions: {
|
||||
x: BALL_SIZE,
|
||||
y: BALL_SIZE,
|
||||
z: BALL_SIZE
|
||||
},
|
||||
gravity: {
|
||||
x: 0.0,
|
||||
y: -9.8,
|
||||
z: 0.0
|
||||
},
|
||||
damping: BALL_DAMPING,
|
||||
angularDamping: BALL_ANGULAR_DAMPING,
|
||||
density: BALL_DENSITY,
|
||||
restitution: BALL_RESTITUTION,
|
||||
dynamic: true,
|
||||
collidesWith: "static,dynamic,otherAvatar,",
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var lineID = Entities.addEntity({
|
||||
type: "PolyLine",
|
||||
name: "TetherballStick Line",
|
||||
color: {
|
||||
red: 0,
|
||||
green: 120,
|
||||
blue: 250
|
||||
},
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
position: ballStartPosition,
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
},
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
|
||||
var actionID = Entities.addAction("offset", ballID, {
|
||||
pointToOffsetFrom: stickStartPosition,
|
||||
linearDistance: ACTION_DISTANCE,
|
||||
linearTimeScale: ACTION_TIMESCALE
|
||||
});
|
||||
|
||||
var STICK_PROPERTIES = {
|
||||
type: 'Model',
|
||||
name: "TetherballStick Stick",
|
||||
modelURL: STICK_MODEL_URL,
|
||||
position: stickStartPosition,
|
||||
rotation: MyAvatar.orientation,
|
||||
dimensions: {
|
||||
x: 0.0651,
|
||||
y: 0.0651,
|
||||
z: 0.5270
|
||||
},
|
||||
script: STICK_SCRIPT_URL,
|
||||
color: {
|
||||
red: 200,
|
||||
green: 0,
|
||||
blue: 20
|
||||
},
|
||||
shapeType: 'box',
|
||||
lifetime: LIFETIME,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true,
|
||||
ignoreIK: false
|
||||
},
|
||||
wearable: {
|
||||
joints: {
|
||||
RightHand: [{
|
||||
x: 0.15539926290512085,
|
||||
y: 0.14493153989315033,
|
||||
z: 0.023641478270292282
|
||||
}, {
|
||||
x: 0.5481458902359009,
|
||||
y: -0.4470711946487427,
|
||||
z: -0.3148134648799896,
|
||||
w: 0.6328644752502441
|
||||
}],
|
||||
LeftHand: [{
|
||||
x: -0.14998853206634521,
|
||||
y: 0.17033983767032623,
|
||||
z: 0.023199155926704407
|
||||
},
|
||||
{
|
||||
x: 0.6623835563659668,
|
||||
y: -0.1671387255191803,
|
||||
z: 0.7071226835250854,
|
||||
w: 0.1823924481868744
|
||||
}]
|
||||
}
|
||||
},
|
||||
ownerID: MyAvatar.sessionUUID,
|
||||
ballID: ballID,
|
||||
lineID: lineID,
|
||||
actionID: actionID,
|
||||
lifetime: LIFETIME,
|
||||
maxDistanceBetweenBallAndStick: ACTION_DISTANCE * MAX_DISTANCE_MULTIPLIER
|
||||
})
|
||||
};
|
||||
|
||||
Entities.addEntity(STICK_PROPERTIES);
|
||||
Script.stop();
|
140
scripts/tutorials/entity_scripts/tetherballStick.js
Normal file
|
@ -0,0 +1,140 @@
|
|||
"use strict";
|
||||
/* jslint vars: true, plusplus: true, forin: true*/
|
||||
/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */
|
||||
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||
//
|
||||
// tetherballStick.js
|
||||
//
|
||||
// Created by Triplelexx on 17/03/04
|
||||
// Updated by MrRoboman on 17/03/26
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Entity script for an equippable stick with a tethered ball
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
(function() {
|
||||
var _this;
|
||||
|
||||
var LIFETIME_IF_LEAVE_DOMAIN = 2;
|
||||
var LINE_WIDTH = 0.02;
|
||||
var COLLISION_SOUND_URL = "http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav";
|
||||
var TIP_OFFSET = 0.26;
|
||||
|
||||
tetherballStick = function() {
|
||||
_this = this;
|
||||
};
|
||||
|
||||
tetherballStick.prototype = {
|
||||
|
||||
getUserData: function() {
|
||||
try {
|
||||
var stickProps = Entities.getEntityProperties(this.entityID);
|
||||
var userData = JSON.parse(stickProps.userData);
|
||||
return userData;
|
||||
} catch (e) {
|
||||
print("Error parsing Tetherball Stick UserData in file " +
|
||||
e.fileName + " on line " + e.lineNumber);
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
|
||||
var userData = this.getUserData();
|
||||
this.ballID = userData.ballID;
|
||||
this.lineID = userData.lineID;
|
||||
this.actionID = userData.actionID;
|
||||
this.maxDistanceBetweenBallAndStick = userData.maxDistanceBetweenBallAndStick;
|
||||
},
|
||||
|
||||
update: function(dt) {
|
||||
_this.drawLine();
|
||||
},
|
||||
|
||||
startEquip: function() {
|
||||
Script.update.disconnect(this.update);
|
||||
},
|
||||
|
||||
continueEquip: function(id, params) {
|
||||
var stickProps = Entities.getEntityProperties(this.entityID);
|
||||
[this.entityID, this.ballID, this.lineID].forEach(function(id) {
|
||||
Entities.editEntity(id, {
|
||||
lifetime: stickProps.age + LIFETIME_IF_LEAVE_DOMAIN
|
||||
});
|
||||
});
|
||||
this.updateOffsetAction();
|
||||
this.capBallDistance();
|
||||
this.drawLine();
|
||||
},
|
||||
|
||||
releaseEquip: function() {
|
||||
var userData = this.getUserData();
|
||||
[this.entityID, this.ballID, this.lineID].forEach(function(id) {
|
||||
Entities.editEntity(id, {
|
||||
lifetime: userData.lifetime
|
||||
});
|
||||
});
|
||||
Script.update.connect(this.update);
|
||||
},
|
||||
|
||||
getTipPosition: function() {
|
||||
var stickProps = Entities.getEntityProperties(this.entityID);
|
||||
var stickFront = Quat.getFront(stickProps.rotation);
|
||||
var frontOffset = Vec3.multiply(stickFront, TIP_OFFSET);
|
||||
var tipPosition = Vec3.sum(stickProps.position, frontOffset);
|
||||
|
||||
return tipPosition;
|
||||
},
|
||||
|
||||
updateOffsetAction: function() {
|
||||
Entities.updateAction(this.ballID, this.actionID, {
|
||||
pointToOffsetFrom: this.getTipPosition()
|
||||
});
|
||||
},
|
||||
|
||||
capBallDistance: function() {
|
||||
var stickProps = Entities.getEntityProperties(this.entityID);
|
||||
var ballProps = Entities.getEntityProperties(this.ballID);
|
||||
var tipPosition = this.getTipPosition();
|
||||
var distance = Vec3.distance(tipPosition, ballProps.position);
|
||||
var maxDistance = this.maxDistanceBetweenBallAndStick;
|
||||
|
||||
if(distance > maxDistance) {
|
||||
var direction = Vec3.normalize(Vec3.subtract(ballProps.position, tipPosition));
|
||||
var newPosition = Vec3.sum(tipPosition, Vec3.multiply(maxDistance, direction));
|
||||
Entities.editEntity(this.ballID, {
|
||||
position: newPosition
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
drawLine: function() {
|
||||
var stickProps = Entities.getEntityProperties(this.entityID);
|
||||
var tipPosition = this.getTipPosition();
|
||||
var ballProps = Entities.getEntityProperties(this.ballID);
|
||||
var cameraQuat = Vec3.multiplyQbyV(Camera.getOrientation(), Vec3.UNIT_NEG_Z);
|
||||
var linePoints = [];
|
||||
var normals = [];
|
||||
var strokeWidths = [];
|
||||
linePoints.push(Vec3.ZERO);
|
||||
normals.push(cameraQuat);
|
||||
strokeWidths.push(LINE_WIDTH);
|
||||
linePoints.push(Vec3.subtract(ballProps.position, tipPosition));
|
||||
normals.push(cameraQuat);
|
||||
strokeWidths.push(LINE_WIDTH);
|
||||
|
||||
var lineProps = Entities.getEntityProperties(this.lineID);
|
||||
Entities.editEntity(this.lineID, {
|
||||
linePoints: linePoints,
|
||||
normals: normals,
|
||||
strokeWidths: strokeWidths,
|
||||
position: tipPosition,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// entity scripts should return a newly constructed object of our type
|
||||
return new tetherballStick();
|
||||
});
|
|
@ -48,8 +48,8 @@
|
|||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include <SimpleEntitySimulation.h>
|
||||
#include <EntityActionInterface.h>
|
||||
#include <EntityActionFactoryInterface.h>
|
||||
#include <EntityDynamicInterface.h>
|
||||
#include <EntityDynamicFactoryInterface.h>
|
||||
#include <WebEntityItem.h>
|
||||
#include <OctreeUtils.h>
|
||||
#include <render/Engine.h>
|
||||
|
@ -365,17 +365,17 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class TestActionFactory : public EntityActionFactoryInterface {
|
||||
class TestActionFactory : public EntityDynamicFactoryInterface {
|
||||
public:
|
||||
virtual EntityActionPointer factory(EntityActionType type,
|
||||
virtual EntityDynamicPointer factory(EntityDynamicType type,
|
||||
const QUuid& id,
|
||||
EntityItemPointer ownerEntity,
|
||||
QVariantMap arguments) override {
|
||||
return EntityActionPointer();
|
||||
return EntityDynamicPointer();
|
||||
}
|
||||
|
||||
|
||||
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override {
|
||||
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
@ -475,7 +475,7 @@ protected:
|
|||
public:
|
||||
//"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126"
|
||||
static void setup() {
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, TestActionFactory>();
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, TestActionFactory>();
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
|
|
50
unpublishedScripts/marketplace/gameTable/README.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
Game Table Documentation
|
||||
=============
|
||||
|
||||
Ready to play? Try out our new gaming table, with five games to choose from: checkers, chess, dominoes, dice, and a deck of cards! Add your own game!
|
||||
|
||||
The gaming table is designed with the following principles:
|
||||
|
||||
1. Gets its list of games from a remote location, and so can be updated without requiring a new download by users.
|
||||
2. New games can be defined in a simple JSON data structure.
|
||||
3. Games are made for using hand controllers. Pieces are physical as much as possible.
|
||||
4. Simple controls: reset game, next game.
|
||||
5. No 'rules' are enforced. It's up to players to respect each other.
|
||||
|
||||
|
||||
ADDING GAMES
|
||||
============
|
||||
|
||||
There are two spawning styles for games, depending on the type of game you're making: 'arranged' spawning and 'pile' spawning.
|
||||
|
||||
For 'arranged' style games, the table divides its surface into tiles based on how many rows are in the arrangement. Currently, the boards must be square (i.e. 8x8 or 10x10). Then it goes through the starting arrangement and pastes the JSON for each piece at the appropriate location. A player number in front of the piece name in the game definition helps pick the right color piece for games where player pieces are similar. Releasing a held piece with 'snapToGrid' on will result in that piece snapping to the nearest available tile.
|
||||
|
||||
JSON GAME PARAMETERS
|
||||
=============
|
||||
(BOTH STYLES)
|
||||
- @gameName - string - what the game is called.```checkers```
|
||||
- @matURL - string - url of picture to put on the table. ```http://mywebsite/checkers.jpg```
|
||||
- @spawnStyle - string - either ```pile``` or ```arranged```.
|
||||
|
||||
(ARRANGED STYLE)
|
||||
- @snapToGrid - boolean - should pieces snap to tiles
|
||||
- @startingArrangement - 2d array - ['playerNumber:pieceName'] - ```["1:rook","2:queen"]```
|
||||
- @pieces - array of object mappings - one object mapping per player -{pieceName:JSONurl} - ```[{"king": "http://mywebsite/chess/king_black.json"},{"king": "http://mywebsite/chess/king_white.json"}]```
|
||||
|
||||
(PILE STYLE)
|
||||
- @identicalPieces - boolean - whether or not the pieces are identical. if ```true```, only the first piece is used and repeatedly spawned.
|
||||
- @howMany - number - the amount of pieces to creates
|
||||
- @pieces - array of strings - ```["http://mywebsite/domino.json"]```
|
||||
|
||||
|
||||
TESTING
|
||||
=============
|
||||
Change the GAMES_LIST_ENDPOINT variable in table.js to point at your array of game .json files on a local or remote server.
|
||||
|
||||
The list should look more or less like this: https://api.myjson.com/bins/437s6
|
||||
|
||||
PUBLISHING
|
||||
=============
|
||||
- As a script: just add createGameTable.js to the marketplace.
|
||||
|
||||
- As an item: run createGameTable.js and select everything, export it to json. Go through the usual marketplace uploading and prep sequence.
|
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1.4 MiB |
BIN
unpublishedScripts/marketplace/gameTable/assets/cards/back.jpg
Normal file
After Width: | Height: | Size: 367 KiB |
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"Entities": [{
|
||||
"clientOnly": 0,
|
||||
"collisionsWillMove": 1,
|
||||
"created": "2016-09-12T18:28:06Z",
|
||||
"description": "hifi:gameTable:piece:deckOfCards",
|
||||
"dimensions": {
|
||||
"x": 0.26210001111030579,
|
||||
"y": 0.10000000149011612,
|
||||
"z": 0.45329999923706055
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -10,
|
||||
"z": 0
|
||||
},
|
||||
"velocity": {
|
||||
"x": 0,
|
||||
"y": -0.9,
|
||||
"z": 0
|
||||
},
|
||||
"friction":1,
|
||||
"restitution":0,
|
||||
"id": "{36bdb632-06be-4d13-b9be-e620ebec6422}",
|
||||
"modelURL": "assets/cards/playing_card.fbx",
|
||||
"name": "Deck of Cards",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"queryAACube": {
|
||||
"scale": 0.53308284282684326,
|
||||
"x": -0.26654142141342163,
|
||||
"y": -0.26654142141342163,
|
||||
"z": -0.26654142141342163
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.9568779468536377,
|
||||
"x": -7.62939453125e-05,
|
||||
"y": -0.29045546054840088,
|
||||
"z": 1.52587890625e-05
|
||||
},
|
||||
"script": "games/deckOfCards/deckOfCards.js",
|
||||
"shapeType": "simple-compound",
|
||||
"type": "Model"
|
||||
}],
|
||||
"Version": 64
|
||||
}
|
After Width: | Height: | Size: 367 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 23 KiB |