diff --git a/examples/grab.js b/examples/grab.js index c6b1b884b7..f64aaa30ee 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -4,7 +4,7 @@ // Created by Eric Levin on May 1, 2015 // Copyright 2015 High Fidelity, Inc. // -// Grab's physically moveable entities with the mouse, by applying a spring force. +// Grab's physically moveable entities with the mouse, by applying a spring force. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -20,7 +20,7 @@ var ANGULAR_DAMPING_RATE = 0.40; // NOTE: to improve readability global variable names start with 'g' var gIsGrabbing = false; var gGrabbedEntity = null; -var gPrevMouse = {x: 0, y: 0}; +var gActionID = null; var gEntityProperties; var gStartPosition; var gStartRotation; @@ -31,20 +31,20 @@ var gPlaneNormal = ZERO_VEC3; // gMaxGrabDistance is a function of the size of the object. var gMaxGrabDistance; -// gGrabMode defines the degrees of freedom of the grab target positions -// relative to gGrabStartPosition options include: +// gGrabMode defines the degrees of freedom of the grab target positions +// relative to gGrabStartPosition options include: // xzPlane (default) // verticalCylinder (SHIFT) // rotate (CONTROL) // Modes to eventually support?: -// xyPlane -// yzPlane +// xyPlane +// yzPlane // polar // elevationAzimuth -var gGrabMode = "xzplane"; +var gGrabMode = "xzplane"; -// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection -// with the move-plane to object center (at the moment the grab is initiated). Future target positions +// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection +// with the move-plane to object center (at the moment the grab is initiated). Future target positions // are relative to the ray's intersection by the same offset. var gGrabOffset = { x: 0, y: 0, z: 0 }; @@ -53,13 +53,14 @@ var gTargetRotation; var gLiftKey = false; // SHIFT var gRotateKey = false; // CONTROL +var gInitialMouse = { x: 0, y: 0 }; var gPreviousMouse = { x: 0, y: 0 }; var gMouseCursorLocation = { x: 0, y: 0 }; var gMouseAtRotateStart = { x: 0, y: 0 }; var gBeaconHeight = 0.10; -var gAngularVelocity = ZERO_VEC3; +// var gAngularVelocity = ZERO_VEC3; // TODO: play sounds again when we aren't leaking AudioInjector threads // var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); @@ -166,7 +167,7 @@ function computeNewGrabPlane() { var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition()); xzOffset.y = 0; gXzDistanceToGrab = Vec3.length(xzOffset); - + if (gGrabMode !== "rotate" && maybeResetMousePosition) { // we reset the mouse position whenever we stop rotating Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y); @@ -177,6 +178,7 @@ function mousePressEvent(event) { if (!event.isLeftButton) { return; } + gInitialMouse = {x: event.x, y: event.y }; gPreviousMouse = {x: event.x, y: event.y }; var pickRay = Camera.computePickRay(event.x, event.y); @@ -193,12 +195,13 @@ function mousePressEvent(event) { var clickedEntity = pickResults.entityID; var entityProperties = Entities.getEntityProperties(clickedEntity) - var objectPosition = entityProperties.position; + gStartPosition = entityProperties.position; + gStartRotation = entityProperties.rotation; var cameraPosition = Camera.getPosition(); gBeaconHeight = Vec3.length(entityProperties.dimensions); gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE; - if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) { + if (Vec3.distance(gStartPosition, cameraPosition) > gMaxGrabDistance) { // don't allow grabs of things far away return; } @@ -209,20 +212,20 @@ function mousePressEvent(event) { gGrabbedEntity = clickedEntity; gCurrentPosition = entityProperties.position; gOriginalGravity = entityProperties.gravity; - gTargetPosition = objectPosition; + gTargetPosition = gStartPosition; // compute the grab point - var nearestPoint = Vec3.subtract(objectPosition, cameraPosition); + var nearestPoint = Vec3.subtract(gStartPosition, cameraPosition); var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction); nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction); gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint); // compute the grab offset - gGrabOffset = Vec3.subtract(objectPosition, gPointOnPlane); + gGrabOffset = Vec3.subtract(gStartPosition, gPointOnPlane); computeNewGrabPlane(); - updateDropLine(objectPosition); + updateDropLine(gStartPosition); // TODO: play sounds again when we aren't leaking AudioInjector threads //Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME }); @@ -235,6 +238,8 @@ function mouseReleaseEvent() { } gIsGrabbing = false + Entities.deleteAction(gGrabbedEntity, gActionID); + gActionID = null; Overlays.editOverlay(gBeacon, { visible: false }); @@ -254,18 +259,24 @@ function mouseMoveEvent(event) { gOriginalGravity = entityProperties.gravity; } + var actionArgs = {}; + if (gGrabMode === "rotate") { var deltaMouse = { x: 0, y: 0 }; - var dx = event.x - gPreviousMouse.x; - var dy = event.y - gPreviousMouse.y; - + var dx = event.x - gInitialMouse.x; + var dy = event.y - gInitialMouse.y; var orientation = Camera.getOrientation(); var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation))); var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); - var axis = Vec3.normalize(axis); - var ROTATE_STRENGTH = 8.0; // magic number tuned by hand - gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis); + axis = Vec3.normalize(axis); + var ROTATE_STRENGTH = 0.4; // magic number tuned by hand + var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy)); + var deltaQ = Quat.angleAxis(angle, axis); + // var qZero = entityProperties.rotation; + var qZero = gStartRotation; + var qOne = Quat.multiply(deltaQ, qZero); + actionArgs = {targetRotation: qOne, angularTimeScale: 0.1}; } else { var newTargetPosition; if (gGrabMode === "verticalCylinder") { @@ -288,9 +299,18 @@ function mouseMoveEvent(event) { } } gTargetPosition = Vec3.sum(newTargetPosition, gGrabOffset); + actionArgs = {targetPosition: gTargetPosition, linearTimeScale: 0.1}; } gPreviousMouse = { x: event.x, y: event.y }; gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() }; + + if (!gActionID) { + gActionID = Entities.addAction("spring", gGrabbedEntity, actionArgs); + } else { + Entities.updateAction(gGrabbedEntity, gActionID, actionArgs); + } + + updateDropLine(gTargetPosition); } function keyReleaseEvent(event) { @@ -313,38 +333,8 @@ function keyPressEvent(event) { computeNewGrabPlane(); } -function update(deltaTime) { - if (!gIsGrabbing) { - return; - } - - var entityProperties = Entities.getEntityProperties(gGrabbedEntity); - gCurrentPosition = entityProperties.position; - if (gGrabMode === "rotate") { - gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE)); - Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, }); - } - - // always push toward linear grab position, even when rotating - var newVelocity = ZERO_VEC3; - var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition); - var delta = Vec3.length(dPosition); - if (delta > CLOSE_ENOUGH) { - var MAX_POSITION_DELTA = 4.0; - if (delta > MAX_POSITION_DELTA) { - dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta); - } - // desired speed is proportional to displacement by the inverse of timescale - // (for critically damped motion) - newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE); - } - Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, }); - updateDropLine(gTargetPosition); -} - Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); -Script.update.connect(update); diff --git a/examples/stick.js b/examples/stick.js new file mode 100644 index 0000000000..ea1f3439a9 --- /dev/null +++ b/examples/stick.js @@ -0,0 +1,92 @@ +// stick.js +// examples +// +// Created by Seth Alves on 2015-6-10 +// Copyright 2015 High Fidelity, Inc. +// +// Allow avatar to hold a stick +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var hand = "left"; +var nullActionID = "00000000-0000-0000-0000-000000000000"; +var controllerID; +var controllerActive; +var stickID = null; +var actionID = nullActionID; +// sometimes if this is run immediately the stick doesn't get created? use a timer. +Script.setTimeout(function() { + stickID = Entities.addEntity({ + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", + compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", + dimensions: {x: .11, y: .11, z: .59}, + position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close + rotation: MyAvatar.orientation, + damping: .1, + collisionsWillMove: true + }); + actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, + hand: hand, + timeScale: 0.15}); +}, 3000); + + +function cleanUp() { + Entities.deleteEntity(stickID); +} + + +function positionStick(stickOrientation) { + var baseOffset = {x: 0.0, y: 0.0, z: -0.9}; + var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset); + Entities.updateAction(stickID, actionID, {relativePosition: offset, + relativeRotation: stickOrientation}); +} + + +function mouseMoveEvent(event) { + if (!stickID || actionID == nullActionID) { + return; + } + var windowCenterX = Window.innerWidth / 2; + var windowCenterY = Window.innerHeight / 2; + var mouseXCenterOffset = event.x - windowCenterX; + var mouseYCenterOffset = event.y - windowCenterY; + var mouseXRatio = mouseXCenterOffset / windowCenterX; + var mouseYRatio = mouseYCenterOffset / windowCenterY; + + var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0); + positionStick(stickOrientation); +} + + +function initControls(){ + if (hand == "right") { + controllerID = 3; // right handed + } else { + controllerID = 4; // left handed + } +} + + +function update(deltaTime){ + var palmPosition = Controller.getSpatialControlPosition(controllerID); + controllerActive = (Vec3.length(palmPosition) > 0); + if(!controllerActive){ + return; + } + + stickOrientation = Controller.getSpatialControlRawRotation(controllerID); + var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0); + stickOrientation = Quat.multiply(stickOrientation, adjustment); + + positionStick(stickOrientation); +} + + +Script.scriptEnding.connect(cleanUp); +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Script.update.connect(update); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3e993450c3..750955dc6a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -100,6 +100,7 @@ #include "ModelPackager.h" #include "Util.h" #include "InterfaceLogging.h" +#include "InterfaceActionFactory.h" #include "avatar/AvatarManager.h" @@ -257,6 +258,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); + DependencyManager::registerInheritance(); Setting::init(); @@ -293,7 +295,8 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); - auto pathUtils = DependencyManager::set(); + auto pathUtils = DependencyManager::set(); + auto actionFactory = DependencyManager::set(); return true; } diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp new file mode 100644 index 0000000000..08afc3581c --- /dev/null +++ b/interface/src/InterfaceActionFactory.cpp @@ -0,0 +1,49 @@ +// +// 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 +#include +#include + +#include "InterfaceActionFactory.h" + + +EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { + EntityActionPointer action = nullptr; + switch (type) { + case ACTION_TYPE_NONE: + return nullptr; + case ACTION_TYPE_PULL_TO_POINT: + action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); + break; + case ACTION_TYPE_SPRING: + action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); + break; + case ACTION_TYPE_HOLD: + action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity); + break; + } + + bool ok = action->updateArguments(arguments); + if (ok) { + ownerEntity->addAction(simulation, action); + return action; + } + + action = nullptr; + return action; +} diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h new file mode 100644 index 0000000000..5848df4635 --- /dev/null +++ b/interface/src/InterfaceActionFactory.h @@ -0,0 +1,28 @@ +// +// InterfaceActionFactory.cpp +// interface/src/ +// +// Created by Seth Alves on 2015-6-10 +// 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_InterfaceActionFactory_h +#define hifi_InterfaceActionFactory_h + +#include "EntityActionFactoryInterface.h" + +class InterfaceActionFactory : public EntityActionFactoryInterface { +public: + InterfaceActionFactory() : EntityActionFactoryInterface() { } + virtual ~InterfaceActionFactory() { } + virtual EntityActionPointer factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments); +}; + +#endif // hifi_InterfaceActionFactory_h diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp new file mode 100644 index 0000000000..1fbb01beb3 --- /dev/null +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -0,0 +1,108 @@ +// +// AvatarActionHold.cpp +// interface/src/avatar/ +// +// Created by Seth Alves 2015-6-9 +// 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/MyAvatar.h" +#include "avatar/AvatarManager.h" + +#include "AvatarActionHold.h" + +AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) : + ObjectActionSpring(id, ownerEntity) { + #if WANT_DEBUG + qDebug() << "AvatarActionHold::AvatarActionHold"; + #endif +} + +AvatarActionHold::~AvatarActionHold() { + #if WANT_DEBUG + qDebug() << "AvatarActionHold::~AvatarActionHold"; + #endif +} + +void AvatarActionHold::updateActionWorker(float deltaTimeStep) { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 palmPosition; + if (_hand == "right") { + palmPosition = myAvatar->getRightPalmPosition(); + } else { + palmPosition = myAvatar->getLeftPalmPosition(); + } + + auto rotation = myAvatar->getWorldAlignedOrientation(); + auto offset = rotation * _relativePosition; + auto position = palmPosition + offset; + rotation *= _relativeRotation; + + lockForWrite(); + _positionalTarget = position; + _rotationalTarget = rotation; + unlock(); + + ObjectActionSpring::updateActionWorker(deltaTimeStep); +} + + +bool AvatarActionHold::updateArguments(QVariantMap arguments) { + bool rPOk = true; + glm::vec3 relativePosition = + EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false); + bool rROk = true; + glm::quat relativeRotation = + EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false); + bool tSOk = true; + float timeScale = + EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false); + bool hOk = true; + QString hand = + EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false); + + lockForWrite(); + if (rPOk) { + _relativePosition = relativePosition; + } else if (!_parametersSet) { + _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f); + } + + if (rROk) { + _relativeRotation = relativeRotation; + } else if (!_parametersSet) { + _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f); + } + + if (tSOk) { + _linearTimeScale = timeScale; + _angularTimeScale = timeScale; + } else if (!_parametersSet) { + _linearTimeScale = 0.2; + _angularTimeScale = 0.2; + } + + if (hOk) { + hand = hand.toLower(); + if (hand == "left") { + _hand = "left"; + } else if (hand == "right") { + _hand = "right"; + } else { + qDebug() << "hold action -- invalid hand argument:" << hand; + _hand = "right"; + } + } else if (!_parametersSet) { + _hand = "right"; + } + + _parametersSet = true; + _positionalTargetSet = true; + _rotationalTargetSet = true; + _active = true; + unlock(); + return true; +} diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h new file mode 100644 index 0000000000..705c751029 --- /dev/null +++ b/interface/src/avatar/AvatarActionHold.h @@ -0,0 +1,35 @@ +// +// AvatarActionHold.h +// interface/src/avatar/ +// +// Created by Seth Alves 2015-6-9 +// 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_AvatarActionHold_h +#define hifi_AvatarActionHold_h + +#include + +#include +#include + +class AvatarActionHold : public ObjectActionSpring { +public: + AvatarActionHold(QUuid id, EntityItemPointer ownerEntity); + virtual ~AvatarActionHold(); + + virtual bool updateArguments(QVariantMap arguments); + virtual void updateActionWorker(float deltaTimeStep); + +private: + glm::vec3 _relativePosition; + glm::quat _relativeRotation; + QString _hand; + bool _parametersSet = false; +}; + +#endif // hifi_AvatarActionHold_h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 98cbc1f845..3e83c6f559 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1012,7 +1012,9 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { checkAndCallPreload(entityID); auto entity = static_cast(_tree)->findEntityByID(entityID); - addEntityToScene(entity); + if (entity) { + addEntityToScene(entity); + } } void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) { diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h new file mode 100644 index 0000000000..9820313aae --- /dev/null +++ b/libraries/entities/src/EntityActionFactoryInterface.h @@ -0,0 +1,33 @@ +// +// EntityActionFactoryInterface.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 +// + +#ifndef hifi_EntityActionFactoryInterface_h +#define hifi_EntityActionFactoryInterface_h + +#include + +#include "EntityActionInterface.h" + +class EntityActionFactoryInterface : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + + public: + EntityActionFactoryInterface() { } + virtual ~EntityActionFactoryInterface() { } + virtual EntityActionPointer factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { assert(false); return nullptr; } +}; + +#endif // hifi_EntityActionFactoryInterface_h diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index f26dd006ff..e97669686c 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -22,6 +22,12 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS if (normalizedActionTypeString == "pulltopoint") { return ACTION_TYPE_PULL_TO_POINT; } + if (normalizedActionTypeString == "spring") { + return ACTION_TYPE_SPRING; + } + if (normalizedActionTypeString == "hold") { + return ACTION_TYPE_HOLD; + } qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; @@ -33,31 +39,37 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { return "none"; case ACTION_TYPE_PULL_TO_POINT: return "pullToPoint"; + case ACTION_TYPE_SPRING: + return "spring"; + case ACTION_TYPE_HOLD: + return "hold"; } assert(false); return "none"; } glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok) { + QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { - qDebug() << objectName << "requires argument:" << argumentName; + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } ok = false; - return vec3(); + return glm::vec3(); } QVariant resultV = arguments[argumentName]; if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { qDebug() << objectName << "argument" << argumentName << "must be a map"; ok = false; - return vec3(); + return glm::vec3(); } QVariantMap resultVM = resultV.toMap(); if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z"; ok = false; - return vec3(); + return glm::vec3(); } QVariant xV = resultVM["x"]; @@ -73,17 +85,65 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian if (!xOk || !yOk || !zOk) { qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float."; ok = false; - return vec3(); + return glm::vec3(); } - return vec3(x, y, z); + 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) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return glm::quat(); + } + + QVariant resultV = arguments[argumentName]; + if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + qDebug() << 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")) { + qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z"; + 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) { + qDebug() << objectName << "argument" << argumentName + << "must be a map with keys of x, y, z, w and values of type float."; + ok = false; + return glm::quat(); + } + + return glm::quat(w, x, y, z); +} float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok) { + QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { - qDebug() << objectName << "requires argument:" << argumentName; + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } ok = false; return 0.0f; } @@ -99,3 +159,18 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa return v; } + +QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return ""; + } + + QVariant vV = arguments[argumentName]; + QString v = vV.toString(); + return v; +} diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 74efae3239..486d3f5948 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -14,12 +14,16 @@ #include +#include "EntityItem.h" + class EntitySimulation; enum EntityActionType { // keep these synchronized with actionTypeFromString and actionTypeToString ACTION_TYPE_NONE, - ACTION_TYPE_PULL_TO_POINT + ACTION_TYPE_PULL_TO_POINT, + ACTION_TYPE_SPRING, + ACTION_TYPE_HOLD }; @@ -32,18 +36,35 @@ public: virtual const EntityItemPointer& getOwnerEntity() const = 0; virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0; virtual bool updateArguments(QVariantMap arguments) = 0; - // virtual QByteArray serialize() = 0; - // static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data); static EntityActionType actionTypeFromString(QString actionTypeString); static QString actionTypeToString(EntityActionType actionType); protected: + virtual glm::vec3 getPosition() = 0; + virtual void setPosition(glm::vec3 position) = 0; + virtual glm::quat getRotation() = 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; + + // these look in the arguments map for a named argument. if it's not found or isn't well formed, + // ok will be set to false (note that it's never set to true -- set it to true before calling these). + // if required is true, failure to extract an argument will cause a warning to be printed. + static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static float extractFloatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static QString extractStringArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); - static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); - static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); }; + typedef std::shared_ptr EntityActionPointer; #endif // hifi_EntityActionInterface_h diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a145c4c236..73f9127361 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -28,13 +28,16 @@ #include "EntityItemID.h" #include "EntityItemProperties.h" #include "EntityItemPropertiesDefaults.h" -#include "EntityActionInterface.h" #include "EntityTypes.h" class EntitySimulation; class EntityTreeElement; class EntityTreeElementExtraEncodeData; +class EntityActionInterface; +typedef std::shared_ptr EntityActionPointer; + + namespace render { class Scene; class PendingChanges; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 836e7f5225..a091c786b7 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -17,6 +17,8 @@ #include "ZoneEntityItem.h" #include "EntitiesLogging.h" #include "EntitySimulation.h" +#include "EntityActionInterface.h" +#include "EntityActionFactoryInterface.h" #include "EntityScriptingInterface.h" @@ -491,12 +493,19 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments) { QUuid actionID = QUuid::createUuid(); + auto actionFactory = DependencyManager::get(); bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + // create this action even if the entity doesn't have physics info. it will often be the + // case that a script adds an action immediately after an object is created, and the physicsInfo + // is computed asynchronously. + // if (!entity->getPhysicsInfo()) { + // return false; + // } EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); if (actionType == ACTION_TYPE_NONE) { return false; } - if (simulation->actionFactory(actionType, actionID, entity, arguments)) { + if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) { return true; } return false; diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index 0c9b3efee6..7d244086e5 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -18,6 +18,7 @@ #include +#include "EntityActionInterface.h" #include "EntityItem.h" #include "EntityTree.h" @@ -56,10 +57,6 @@ public: friend class EntityTree; - virtual EntityActionPointer actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { return nullptr; } virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; } virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; } virtual void removeActions(QList actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; } diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 6ff4098ba8..ee7ba9ce7c 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -24,7 +24,14 @@ ObjectAction::~ObjectAction() { } void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { - qDebug() << "ObjectAction::updateAction called"; + if (!_active) { + return; + } + if (!_ownerEntity) { + qDebug() << "ObjectActionPullToPoint::updateAction no owner entity"; + return; + } + updateActionWorker(deltaTimeStep); } void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { @@ -33,3 +40,87 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const { simulation->removeAction(_id); } + +btRigidBody* ObjectAction::getRigidBody() { + if (!_ownerEntity) { + return nullptr; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + return nullptr; + } + ObjectMotionState* motionState = static_cast(physicsInfo); + return motionState->getRigidBody(); +} + +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(); +} diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 10b086c07d..0fd7383e6f 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -13,12 +13,17 @@ #ifndef hifi_ObjectAction_h #define hifi_ObjectAction_h -#include - #include +#include + #include +#include "ObjectMotionState.h" +#include "BulletUtil.h" +#include "EntityActionInterface.h" + + class ObjectAction : public btActionInterface, public EntityActionInterface { public: ObjectAction(QUuid id, EntityItemPointer ownerEntity); @@ -30,6 +35,9 @@ public: virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } virtual bool updateArguments(QVariantMap arguments) { return false; } + // this is called from updateAction and should be overridden by subclasses + virtual void updateActionWorker(float deltaTimeStep) {} + // these are from btActionInterface virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); virtual void debugDraw(btIDebugDraw* debugDrawer); @@ -39,6 +47,16 @@ private: QReadWriteLock _lock; protected: + virtual btRigidBody* getRigidBody(); + virtual glm::vec3 getPosition(); + virtual void setPosition(glm::vec3 position); + virtual glm::quat getRotation(); + virtual void setRotation(glm::quat rotation); + virtual glm::vec3 getLinearVelocity(); + virtual void setLinearVelocity(glm::vec3 linearVelocity); + virtual glm::vec3 getAngularVelocity(); + virtual void setAngularVelocity(glm::vec3 angularVelocity); + bool tryLockForRead() { return _lock.tryLockForRead(); } void lockForWrite() { _lock.lockForWrite(); } void unlock() { _lock.unlock(); } diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp index 78f202a24f..053bef6a03 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.cpp +++ b/libraries/physics/src/ObjectActionPullToPoint.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ObjectMotionState.h" -#include "BulletUtil.h" - #include "ObjectActionPullToPoint.h" ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) : @@ -27,28 +24,34 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() { #endif } -void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { +void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) { if (!tryLockForRead()) { // don't risk hanging the thread running the physics simulation return; } - void* physicsInfo = _ownerEntity->getPhysicsInfo(); - if (_active && physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* rigidBody = motionState->getRigidBody(); - if (rigidBody) { - glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); - float offsetLength = glm::length(offset); - if (offsetLength > IGNORE_POSITION_DELTA) { - glm::vec3 newVelocity = glm::normalize(offset) * _speed; - rigidBody->setLinearVelocity(glmToBullet(newVelocity)); - rigidBody->activate(); - } else { - rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); - } - } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + unlock(); + return; } + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (!rigidBody) { + unlock(); + return; + } + + glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * _speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); + } + unlock(); } diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h index 3aca70d640..2596f13515 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.h +++ b/libraries/physics/src/ObjectActionPullToPoint.h @@ -23,7 +23,7 @@ public: virtual ~ObjectActionPullToPoint(); virtual bool updateArguments(QVariantMap arguments); - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); + virtual void updateActionWorker(float deltaTimeStep); private: diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp new file mode 100644 index 0000000000..8eb4f7f652 --- /dev/null +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -0,0 +1,144 @@ +// +// ObjectActionSpring.cpp +// libraries/physics/src +// +// Created by Seth Alves 2015-6-5 +// 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 "ObjectActionSpring.h" + +ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) : + ObjectAction(id, ownerEntity) { + #if WANT_DEBUG + qDebug() << "ObjectActionSpring::ObjectActionSpring"; + #endif +} + +ObjectActionSpring::~ObjectActionSpring() { + #if WANT_DEBUG + qDebug() << "ObjectActionSpring::~ObjectActionSpring"; + #endif +} + +void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { + if (!tryLockForRead()) { + // don't risk hanging the thread running the physics simulation + qDebug() << "ObjectActionSpring::updateActionWorker lock failed"; + return; + } + + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + unlock(); + return; + } + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (!rigidBody) { + unlock(); + qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody"; + return; + } + + // handle the linear part + if (_positionalTargetSet) { + glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + float speed = offsetLength / _linearTimeScale; + + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f))); + } + } + + // handle rotation + if (_rotationalTargetSet) { + glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation()); + // if qZero and qOne are too close to each other, we can get NaN for angle. + auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget); + const float almostOne = 0.99999f; + if (glm::abs(alignmentDot) < almostOne) { + glm::quat target = _rotationalTarget; + if (alignmentDot < 0) { + target = -target; + } + glm::quat qZeroInverse = glm::inverse(bodyRotation); + glm::quat deltaQ = target * qZeroInverse; + glm::vec3 axis = glm::axis(deltaQ); + float angle = glm::angle(deltaQ); + assert(!isNaN(angle)); + glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); + rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); + rigidBody->activate(); + } else { + rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f))); + } + } + + unlock(); +} + + +bool ObjectActionSpring::updateArguments(QVariantMap arguments) { + // targets are required, spring-constants are optional + bool ptOk = true; + glm::vec3 positionalTarget = + EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false); + bool pscOk = true; + float linearTimeScale = + EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", pscOk, false); + if (ptOk && pscOk && linearTimeScale <= 0.0f) { + qDebug() << "spring action -- linearTimeScale must be greater than zero."; + return false; + } + + bool rtOk = true; + glm::quat rotationalTarget = + EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false); + bool rscOk = true; + float angularTimeScale = + EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", rscOk, false); + + if (!ptOk && !rtOk) { + qDebug() << "spring action requires either targetPosition or targetRotation argument"; + return false; + } + + lockForWrite(); + + _positionalTargetSet = _rotationalTargetSet = false; + + if (ptOk) { + _positionalTarget = positionalTarget; + _positionalTargetSet = true; + + if (pscOk) { + _linearTimeScale = linearTimeScale; + } else { + _linearTimeScale = 0.1f; + } + } + + if (rtOk) { + _rotationalTarget = rotationalTarget; + _rotationalTargetSet = true; + + if (rscOk) { + _angularTimeScale = angularTimeScale; + } else { + _angularTimeScale = 0.1f; + } + } + + _active = true; + unlock(); + return true; +} diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h new file mode 100644 index 0000000000..9f3df0fdf8 --- /dev/null +++ b/libraries/physics/src/ObjectActionSpring.h @@ -0,0 +1,39 @@ +// +// ObjectActionSpring.h +// libraries/physics/src +// +// Created by Seth Alves 2015-6-5 +// 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_ObjectActionSpring_h +#define hifi_ObjectActionSpring_h + +#include + +#include +#include "ObjectAction.h" + +class ObjectActionSpring : public ObjectAction { +public: + ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity); + virtual ~ObjectActionSpring(); + + virtual bool updateArguments(QVariantMap arguments); + virtual void updateActionWorker(float deltaTimeStep); + +protected: + + glm::vec3 _positionalTarget; + float _linearTimeScale; + bool _positionalTargetSet; + + glm::quat _rotationalTarget; + float _angularTimeScale; + bool _rotationalTargetSet; +}; + +#endif // hifi_ObjectActionSpring_h diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 711c5e49da..c68b993fe2 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -9,10 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + + #include "PhysicsHelpers.h" #include "PhysicsLogging.h" #include "ShapeManager.h" -#include "ObjectActionPullToPoint.h" #include "PhysicalEntitySimulation.h" @@ -234,29 +235,6 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE } } -EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { - EntityActionPointer action = nullptr; - switch (type) { - case ACTION_TYPE_NONE: - return nullptr; - case ACTION_TYPE_PULL_TO_POINT: - action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); - break; - } - - bool ok = action->updateArguments(arguments); - if (ok) { - ownerEntity->addAction(this, action); - return action; - } - - action = nullptr; - return action; -} - void PhysicalEntitySimulation::applyActionChanges() { if (_physicsEngine) { foreach (EntityActionPointer actionToAdd, _actionsToAdd) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 82b0f8ad51..81ab9f5cce 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -32,10 +32,6 @@ public: void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender); - virtual EntityActionPointer actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments); virtual void applyActionChanges(); protected: // only called by EntitySimulation