mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 11:58:10 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix-dont-send-non-entity-edits-over-wire
This commit is contained in:
commit
03f416ff4d
23 changed files with 2238 additions and 207 deletions
|
@ -16,7 +16,6 @@ Item {
|
||||||
property var parentStackItem: null
|
property var parentStackItem: null
|
||||||
property int headerHeight: 70
|
property int headerHeight: 70
|
||||||
property string url
|
property string url
|
||||||
property alias address: displayUrl.text //for compatibility
|
|
||||||
property string scriptURL
|
property string scriptURL
|
||||||
property alias eventBridge: eventBridgeWrapper.eventBridge
|
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||||
property bool keyboardEnabled: HMD.active
|
property bool keyboardEnabled: HMD.active
|
||||||
|
@ -82,6 +81,7 @@ Item {
|
||||||
color: hifi.colors.baseGray
|
color: hifi.colors.baseGray
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
verticalAlignment: Text.AlignLeft
|
verticalAlignment: Text.AlignLeft
|
||||||
|
text: webview.url
|
||||||
anchors {
|
anchors {
|
||||||
top: nav.bottom
|
top: nav.bottom
|
||||||
horizontalCenter: parent.horizontalCenter;
|
horizontalCenter: parent.horizontalCenter;
|
||||||
|
@ -159,7 +159,6 @@ Item {
|
||||||
function loadUrl(url) {
|
function loadUrl(url) {
|
||||||
webview.url = url
|
webview.url = url
|
||||||
web.url = webview.url;
|
web.url = webview.url;
|
||||||
web.address = webview.url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInitialPage(url) {
|
function onInitialPage(url) {
|
||||||
|
@ -253,7 +252,6 @@ Item {
|
||||||
});
|
});
|
||||||
|
|
||||||
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
|
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
|
||||||
web.address = url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFeaturePermissionRequested: {
|
onFeaturePermissionRequested: {
|
||||||
|
@ -279,7 +277,6 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WebEngineView.LoadSucceededStatus == loadRequest.status) {
|
if (WebEngineView.LoadSucceededStatus == loadRequest.status) {
|
||||||
web.address = webview.url;
|
|
||||||
if (startingUp) {
|
if (startingUp) {
|
||||||
web.initialPage = webview.url;
|
web.initialPage = webview.url;
|
||||||
startingUp = false;
|
startingUp = false;
|
||||||
|
@ -297,6 +294,7 @@ Item {
|
||||||
HiFiControls.Keyboard {
|
HiFiControls.Keyboard {
|
||||||
id: keyboard
|
id: keyboard
|
||||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||||
|
numeric: parent.punctuationMode
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
@ -307,7 +305,6 @@ Item {
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
web.isDesktop = (typeof desktop !== "undefined");
|
web.isDesktop = (typeof desktop !== "undefined");
|
||||||
address = url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
|
|
|
@ -44,6 +44,7 @@ Rectangle {
|
||||||
property var activeTab: "nearbyTab";
|
property var activeTab: "nearbyTab";
|
||||||
property bool currentlyEditingDisplayName: false
|
property bool currentlyEditingDisplayName: false
|
||||||
property bool punctuationMode: false;
|
property bool punctuationMode: false;
|
||||||
|
property var eventBridge;
|
||||||
|
|
||||||
HifiConstants { id: hifi; }
|
HifiConstants { id: hifi; }
|
||||||
|
|
||||||
|
@ -1036,139 +1037,16 @@ Rectangle {
|
||||||
}
|
}
|
||||||
} // Keyboard
|
} // Keyboard
|
||||||
|
|
||||||
Item {
|
HifiControls.TabletWebView {
|
||||||
id: webViewContainer;
|
eventBridge: pal.eventBridge;
|
||||||
anchors.fill: parent;
|
id: userInfoViewer;
|
||||||
|
anchors {
|
||||||
Rectangle {
|
top: parent.top;
|
||||||
id: navigationContainer;
|
bottom: parent.bottom;
|
||||||
visible: userInfoViewer.visible;
|
left: parent.left;
|
||||||
height: 60;
|
right: parent.right;
|
||||||
anchors {
|
|
||||||
top: parent.top;
|
|
||||||
left: parent.left;
|
|
||||||
right: parent.right;
|
|
||||||
}
|
|
||||||
color: hifi.colors.faintGray;
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: backButton
|
|
||||||
anchors {
|
|
||||||
top: parent.top;
|
|
||||||
left: parent.left;
|
|
||||||
}
|
|
||||||
height: parent.height - addressBar.height;
|
|
||||||
width: parent.width/2;
|
|
||||||
|
|
||||||
FiraSansSemiBold {
|
|
||||||
// Properties
|
|
||||||
text: "BACK";
|
|
||||||
elide: Text.ElideRight;
|
|
||||||
// Anchors
|
|
||||||
anchors.fill: parent;
|
|
||||||
// Text Size
|
|
||||||
size: 16;
|
|
||||||
// Text Positioning
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter;
|
|
||||||
// Style
|
|
||||||
color: backButtonMouseArea.containsMouse || !userInfoViewer.canGoBack ? hifi.colors.lightGray : hifi.colors.darkGray;
|
|
||||||
MouseArea {
|
|
||||||
id: backButtonMouseArea;
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: enabled
|
|
||||||
onClicked: {
|
|
||||||
if (userInfoViewer.canGoBack) {
|
|
||||||
userInfoViewer.goBack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: closeButtonContainer
|
|
||||||
anchors {
|
|
||||||
top: parent.top;
|
|
||||||
right: parent.right;
|
|
||||||
}
|
|
||||||
height: parent.height - addressBar.height;
|
|
||||||
width: parent.width/2;
|
|
||||||
|
|
||||||
FiraSansSemiBold {
|
|
||||||
id: closeButton;
|
|
||||||
// Properties
|
|
||||||
text: "CLOSE";
|
|
||||||
elide: Text.ElideRight;
|
|
||||||
// Anchors
|
|
||||||
anchors.fill: parent;
|
|
||||||
// Text Size
|
|
||||||
size: 16;
|
|
||||||
// Text Positioning
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter;
|
|
||||||
// Style
|
|
||||||
color: hifi.colors.redHighlight;
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: enabled
|
|
||||||
onClicked: userInfoViewer.visible = false;
|
|
||||||
onEntered: closeButton.color = hifi.colors.redAccent;
|
|
||||||
onExited: closeButton.color = hifi.colors.redHighlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: addressBar
|
|
||||||
anchors {
|
|
||||||
top: closeButtonContainer.bottom;
|
|
||||||
left: parent.left;
|
|
||||||
right: parent.right;
|
|
||||||
}
|
|
||||||
height: 30;
|
|
||||||
width: parent.width;
|
|
||||||
|
|
||||||
FiraSansRegular {
|
|
||||||
// Properties
|
|
||||||
text: userInfoViewer.url;
|
|
||||||
elide: Text.ElideRight;
|
|
||||||
// Anchors
|
|
||||||
anchors.fill: parent;
|
|
||||||
anchors.leftMargin: 5;
|
|
||||||
// Text Size
|
|
||||||
size: 14;
|
|
||||||
// Text Positioning
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignLeft;
|
|
||||||
// Style
|
|
||||||
color: hifi.colors.lightGray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: webViewBackground;
|
|
||||||
color: "white";
|
|
||||||
visible: userInfoViewer.visible;
|
|
||||||
anchors {
|
|
||||||
top: navigationContainer.bottom;
|
|
||||||
bottom: parent.bottom;
|
|
||||||
left: parent.left;
|
|
||||||
right: parent.right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HifiControls.WebView {
|
|
||||||
id: userInfoViewer;
|
|
||||||
anchors {
|
|
||||||
top: navigationContainer.bottom;
|
|
||||||
bottom: parent.bottom;
|
|
||||||
left: parent.left;
|
|
||||||
right: parent.right;
|
|
||||||
}
|
|
||||||
visible: false;
|
|
||||||
}
|
}
|
||||||
|
visible: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timer used when selecting nearbyTable rows that aren't yet present in the model
|
// Timer used when selecting nearbyTable rows that aren't yet present in the model
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include <ObjectActionSpring.h>
|
#include <ObjectActionSpring.h>
|
||||||
#include <ObjectActionTravelOriented.h>
|
#include <ObjectActionTravelOriented.h>
|
||||||
#include <ObjectConstraintHinge.h>
|
#include <ObjectConstraintHinge.h>
|
||||||
|
#include <ObjectConstraintSlider.h>
|
||||||
|
#include <ObjectConstraintBallSocket.h>
|
||||||
|
#include <ObjectConstraintConeTwist.h>
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
|
||||||
#include "InterfaceDynamicFactory.h"
|
#include "InterfaceDynamicFactory.h"
|
||||||
|
@ -38,9 +41,15 @@ EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid
|
||||||
return std::make_shared<ObjectConstraintHinge>(id, ownerEntity);
|
return std::make_shared<ObjectConstraintHinge>(id, ownerEntity);
|
||||||
case DYNAMIC_TYPE_FAR_GRAB:
|
case DYNAMIC_TYPE_FAR_GRAB:
|
||||||
return std::make_shared<AvatarActionFarGrab>(id, ownerEntity);
|
return std::make_shared<AvatarActionFarGrab>(id, ownerEntity);
|
||||||
|
case DYNAMIC_TYPE_SLIDER:
|
||||||
|
return std::make_shared<ObjectConstraintSlider>(id, ownerEntity);
|
||||||
|
case DYNAMIC_TYPE_BALL_SOCKET:
|
||||||
|
return std::make_shared<ObjectConstraintBallSocket>(id, ownerEntity);
|
||||||
|
case DYNAMIC_TYPE_CONE_TWIST:
|
||||||
|
return std::make_shared<ObjectConstraintConeTwist>(id, ownerEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type");
|
qDebug() << "Unknown entity dynamic type";
|
||||||
return EntityDynamicPointer();
|
return EntityDynamicPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,8 @@ void AvatarActionHold::prepareForPhysicsSimulation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
||||||
glm::vec3& linearVelocity, glm::vec3& angularVelocity) {
|
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
|
||||||
|
float& linearTimeScale, float& angularTimeScale) {
|
||||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
||||||
|
|
||||||
|
@ -213,6 +214,9 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
|
||||||
|
|
||||||
// update linearVelocity based on offset via _relativePosition;
|
// update linearVelocity based on offset via _relativePosition;
|
||||||
linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition);
|
linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition);
|
||||||
|
|
||||||
|
linearTimeScale = _linearTimeScale;
|
||||||
|
angularTimeScale = _angularTimeScale;
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -38,7 +38,8 @@ public:
|
||||||
|
|
||||||
bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
|
bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
|
||||||
virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
||||||
glm::vec3& linearVelocity, glm::vec3& angularVelocity) override;
|
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
|
||||||
|
float& linearTimeScale, float& angularTimeScale) override;
|
||||||
|
|
||||||
virtual void prepareForPhysicsSimulation() override;
|
virtual void prepareForPhysicsSimulation() override;
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,15 @@ EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicT
|
||||||
if (normalizedDynamicTypeString == "fargrab") {
|
if (normalizedDynamicTypeString == "fargrab") {
|
||||||
return DYNAMIC_TYPE_FAR_GRAB;
|
return DYNAMIC_TYPE_FAR_GRAB;
|
||||||
}
|
}
|
||||||
|
if (normalizedDynamicTypeString == "slider") {
|
||||||
|
return DYNAMIC_TYPE_SLIDER;
|
||||||
|
}
|
||||||
|
if (normalizedDynamicTypeString == "ballsocket") {
|
||||||
|
return DYNAMIC_TYPE_BALL_SOCKET;
|
||||||
|
}
|
||||||
|
if (normalizedDynamicTypeString == "conetwist") {
|
||||||
|
return DYNAMIC_TYPE_CONE_TWIST;
|
||||||
|
}
|
||||||
|
|
||||||
qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name"
|
qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name"
|
||||||
<< dynamicTypeString;
|
<< dynamicTypeString;
|
||||||
|
@ -139,6 +148,12 @@ QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicTyp
|
||||||
return "hinge";
|
return "hinge";
|
||||||
case DYNAMIC_TYPE_FAR_GRAB:
|
case DYNAMIC_TYPE_FAR_GRAB:
|
||||||
return "far-grab";
|
return "far-grab";
|
||||||
|
case DYNAMIC_TYPE_SLIDER:
|
||||||
|
return "slider";
|
||||||
|
case DYNAMIC_TYPE_BALL_SOCKET:
|
||||||
|
return "ball-socket";
|
||||||
|
case DYNAMIC_TYPE_CONE_TWIST:
|
||||||
|
return "cone-twist";
|
||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
return "none";
|
return "none";
|
||||||
|
|
|
@ -31,7 +31,10 @@ enum EntityDynamicType {
|
||||||
DYNAMIC_TYPE_HOLD = 3000,
|
DYNAMIC_TYPE_HOLD = 3000,
|
||||||
DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000,
|
DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000,
|
||||||
DYNAMIC_TYPE_HINGE = 5000,
|
DYNAMIC_TYPE_HINGE = 5000,
|
||||||
DYNAMIC_TYPE_FAR_GRAB = 6000
|
DYNAMIC_TYPE_FAR_GRAB = 6000,
|
||||||
|
DYNAMIC_TYPE_SLIDER = 7000,
|
||||||
|
DYNAMIC_TYPE_BALL_SOCKET = 8000,
|
||||||
|
DYNAMIC_TYPE_CONE_TWIST = 9000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityData:
|
case PacketType::EntityData:
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return VERSION_ENTITIES_HINGE_CONSTRAINT;
|
return VERSION_ENTITIES_BULLET_DYNAMICS;
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
||||||
case PacketType::AvatarIdentity:
|
case PacketType::AvatarIdentity:
|
||||||
|
|
|
@ -208,6 +208,7 @@ const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66;
|
||||||
const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67;
|
const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67;
|
||||||
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
|
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
|
||||||
const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
|
const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
|
||||||
|
const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70;
|
||||||
|
|
||||||
enum class EntityQueryPacketVersion: PacketVersion {
|
enum class EntityQueryPacketVersion: PacketVersion {
|
||||||
JSONFilter = 18,
|
JSONFilter = 18,
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
const float SPRING_MAX_SPEED = 10.0f;
|
const float SPRING_MAX_SPEED = 10.0f;
|
||||||
|
const float MAX_SPRING_TIMESCALE = 600.0f; // 10 min is a long time
|
||||||
|
|
||||||
const uint16_t ObjectActionSpring::springVersion = 1;
|
const uint16_t ObjectActionSpring::springVersion = 1;
|
||||||
|
|
||||||
|
@ -41,12 +42,65 @@ ObjectActionSpring::~ObjectActionSpring() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpatiallyNestablePointer ObjectActionSpring::getOther() {
|
||||||
|
SpatiallyNestablePointer other;
|
||||||
|
withWriteLock([&]{
|
||||||
|
if (_otherID == QUuid()) {
|
||||||
|
// no other
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
other = _other.lock();
|
||||||
|
if (other && other->getID() == _otherID) {
|
||||||
|
// other is already up-to-date
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (other) {
|
||||||
|
// we have a pointer to other, but it's wrong
|
||||||
|
other.reset();
|
||||||
|
_other.reset();
|
||||||
|
}
|
||||||
|
// we have an other-id but no pointer to other cached
|
||||||
|
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
|
||||||
|
if (!parentFinder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EntityItemPointer ownerEntity = _ownerEntity.lock();
|
||||||
|
if (!ownerEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool success;
|
||||||
|
_other = parentFinder->find(_otherID, success, ownerEntity->getParentTree());
|
||||||
|
if (success) {
|
||||||
|
other = _other.lock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
|
||||||
bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
||||||
glm::vec3& linearVelocity, glm::vec3& angularVelocity) {
|
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
|
||||||
rotation = _desiredRotationalTarget;
|
float& linearTimeScale, float& angularTimeScale) {
|
||||||
position = _desiredPositionalTarget;
|
SpatiallyNestablePointer other = getOther();
|
||||||
linearVelocity = glm::vec3();
|
withReadLock([&]{
|
||||||
angularVelocity = glm::vec3();
|
linearTimeScale = _linearTimeScale;
|
||||||
|
angularTimeScale = _angularTimeScale;
|
||||||
|
|
||||||
|
if (!_otherID.isNull()) {
|
||||||
|
if (other) {
|
||||||
|
rotation = _desiredRotationalTarget * other->getRotation();
|
||||||
|
position = other->getRotation() * _desiredPositionalTarget + other->getPosition();
|
||||||
|
} else {
|
||||||
|
// we should have an "other" but can't find it, so disable the spring.
|
||||||
|
linearTimeScale = FLT_MAX;
|
||||||
|
angularTimeScale = FLT_MAX;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rotation = _desiredRotationalTarget;
|
||||||
|
position = _desiredPositionalTarget;
|
||||||
|
}
|
||||||
|
linearVelocity = glm::vec3();
|
||||||
|
angularVelocity = glm::vec3();
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +115,10 @@ bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) {
|
||||||
glm::vec3 linearVelocity;
|
glm::vec3 linearVelocity;
|
||||||
glm::vec3 angularVelocity;
|
glm::vec3 angularVelocity;
|
||||||
|
|
||||||
bool valid = false;
|
bool linearValid = false;
|
||||||
int springCount = 0;
|
int linearSpringCount = 0;
|
||||||
|
bool angularValid = false;
|
||||||
|
int angularSpringCount = 0;
|
||||||
|
|
||||||
QList<EntityDynamicPointer> springDerivedActions;
|
QList<EntityDynamicPointer> springDerivedActions;
|
||||||
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_SPRING));
|
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_SPRING));
|
||||||
|
@ -73,41 +129,55 @@ bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) {
|
||||||
std::shared_ptr<ObjectActionSpring> springAction = std::static_pointer_cast<ObjectActionSpring>(action);
|
std::shared_ptr<ObjectActionSpring> springAction = std::static_pointer_cast<ObjectActionSpring>(action);
|
||||||
glm::quat rotationForAction;
|
glm::quat rotationForAction;
|
||||||
glm::vec3 positionForAction;
|
glm::vec3 positionForAction;
|
||||||
glm::vec3 linearVelocityForAction, angularVelocityForAction;
|
glm::vec3 linearVelocityForAction;
|
||||||
bool success = springAction->getTarget(deltaTimeStep, rotationForAction,
|
glm::vec3 angularVelocityForAction;
|
||||||
positionForAction, linearVelocityForAction,
|
float linearTimeScale;
|
||||||
angularVelocityForAction);
|
float angularTimeScale;
|
||||||
|
bool success = springAction->getTarget(deltaTimeStep,
|
||||||
|
rotationForAction, positionForAction,
|
||||||
|
linearVelocityForAction, angularVelocityForAction,
|
||||||
|
linearTimeScale, angularTimeScale);
|
||||||
if (success) {
|
if (success) {
|
||||||
springCount ++;
|
if (angularTimeScale < MAX_SPRING_TIMESCALE) {
|
||||||
if (springAction.get() == this) {
|
angularValid = true;
|
||||||
// only use the rotation for this action
|
angularSpringCount++;
|
||||||
valid = true;
|
angularVelocity += angularVelocityForAction;
|
||||||
rotation = rotationForAction;
|
if (springAction.get() == this) {
|
||||||
|
// only use the rotation for this action
|
||||||
|
rotation = rotationForAction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
position += positionForAction;
|
if (linearTimeScale < MAX_SPRING_TIMESCALE) {
|
||||||
linearVelocity += linearVelocityForAction;
|
linearValid = true;
|
||||||
angularVelocity += angularVelocityForAction;
|
linearSpringCount++;
|
||||||
|
position += positionForAction;
|
||||||
|
linearVelocity += linearVelocityForAction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid && springCount > 0) {
|
if ((angularValid && angularSpringCount > 0) || (linearValid && linearSpringCount > 0)) {
|
||||||
position /= springCount;
|
|
||||||
linearVelocity /= springCount;
|
|
||||||
angularVelocity /= springCount;
|
|
||||||
|
|
||||||
withWriteLock([&]{
|
withWriteLock([&]{
|
||||||
_positionalTarget = position;
|
if (linearValid && linearSpringCount > 0) {
|
||||||
_rotationalTarget = rotation;
|
position /= linearSpringCount;
|
||||||
_linearVelocityTarget = linearVelocity;
|
linearVelocity /= linearSpringCount;
|
||||||
_angularVelocityTarget = angularVelocity;
|
_positionalTarget = position;
|
||||||
_positionalTargetSet = true;
|
_linearVelocityTarget = linearVelocity;
|
||||||
_rotationalTargetSet = true;
|
_positionalTargetSet = true;
|
||||||
_active = true;
|
_active = true;
|
||||||
|
}
|
||||||
|
if (angularValid && angularSpringCount > 0) {
|
||||||
|
angularVelocity /= angularSpringCount;
|
||||||
|
_rotationalTarget = rotation;
|
||||||
|
_angularVelocityTarget = angularVelocity;
|
||||||
|
_rotationalTargetSet = true;
|
||||||
|
_active = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return valid;
|
return linearValid || angularValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,8 +203,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float MAX_TIMESCALE = 600.0f; // 10 min is a long time
|
if (_linearTimeScale < MAX_SPRING_TIMESCALE) {
|
||||||
if (_linearTimeScale < MAX_TIMESCALE) {
|
|
||||||
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
|
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
|
||||||
btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget);
|
btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget);
|
||||||
float offsetLength = offset.length();
|
float offsetLength = offset.length();
|
||||||
|
@ -150,7 +219,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
|
||||||
rigidBody->setLinearVelocity(targetVelocity);
|
rigidBody->setLinearVelocity(targetVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_angularTimeScale < MAX_TIMESCALE) {
|
if (_angularTimeScale < MAX_SPRING_TIMESCALE) {
|
||||||
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
|
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
btQuaternion bodyRotation = rigidBody->getOrientation();
|
btQuaternion bodyRotation = rigidBody->getOrientation();
|
||||||
|
@ -189,6 +258,8 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
||||||
float linearTimeScale;
|
float linearTimeScale;
|
||||||
glm::quat rotationalTarget;
|
glm::quat rotationalTarget;
|
||||||
float angularTimeScale;
|
float angularTimeScale;
|
||||||
|
QUuid otherID;
|
||||||
|
|
||||||
|
|
||||||
bool needUpdate = false;
|
bool needUpdate = false;
|
||||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||||
|
@ -218,11 +289,19 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
||||||
angularTimeScale = _angularTimeScale;
|
angularTimeScale = _angularTimeScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
otherID = QUuid(EntityDynamicInterface::extractStringArgument("spring action",
|
||||||
|
arguments, "otherID", ok, false));
|
||||||
|
if (!ok) {
|
||||||
|
otherID = _otherID;
|
||||||
|
}
|
||||||
|
|
||||||
if (somethingChanged ||
|
if (somethingChanged ||
|
||||||
positionalTarget != _desiredPositionalTarget ||
|
positionalTarget != _desiredPositionalTarget ||
|
||||||
linearTimeScale != _linearTimeScale ||
|
linearTimeScale != _linearTimeScale ||
|
||||||
rotationalTarget != _desiredRotationalTarget ||
|
rotationalTarget != _desiredRotationalTarget ||
|
||||||
angularTimeScale != _angularTimeScale) {
|
angularTimeScale != _angularTimeScale ||
|
||||||
|
otherID != _otherID) {
|
||||||
// something changed
|
// something changed
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
|
@ -234,6 +313,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
||||||
_linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale));
|
_linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale));
|
||||||
_desiredRotationalTarget = rotationalTarget;
|
_desiredRotationalTarget = rotationalTarget;
|
||||||
_angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
|
_angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
|
||||||
|
_otherID = otherID;
|
||||||
_active = true;
|
_active = true;
|
||||||
|
|
||||||
auto ownerEntity = _ownerEntity.lock();
|
auto ownerEntity = _ownerEntity.lock();
|
||||||
|
@ -256,6 +336,8 @@ QVariantMap ObjectActionSpring::getArguments() {
|
||||||
|
|
||||||
arguments["targetRotation"] = glmToQMap(_desiredRotationalTarget);
|
arguments["targetRotation"] = glmToQMap(_desiredRotationalTarget);
|
||||||
arguments["angularTimeScale"] = _angularTimeScale;
|
arguments["angularTimeScale"] = _angularTimeScale;
|
||||||
|
|
||||||
|
arguments["otherID"] = _otherID;
|
||||||
});
|
});
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
@ -270,6 +352,7 @@ void ObjectActionSpring::serializeParameters(QDataStream& dataStream) const {
|
||||||
dataStream << _rotationalTargetSet;
|
dataStream << _rotationalTargetSet;
|
||||||
dataStream << localTimeToServerTime(_expires);
|
dataStream << localTimeToServerTime(_expires);
|
||||||
dataStream << _tag;
|
dataStream << _tag;
|
||||||
|
dataStream << _otherID;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +385,8 @@ void ObjectActionSpring::deserializeParameters(QByteArray serializedArguments, Q
|
||||||
|
|
||||||
dataStream >> _tag;
|
dataStream >> _tag;
|
||||||
|
|
||||||
|
dataStream >> _otherID;
|
||||||
|
|
||||||
_active = true;
|
_active = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ public:
|
||||||
virtual void deserialize(QByteArray serializedArguments) override;
|
virtual void deserialize(QByteArray serializedArguments) override;
|
||||||
|
|
||||||
virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
|
||||||
glm::vec3& linearVelocity, glm::vec3& angularVelocity);
|
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
|
||||||
|
float& linearTimeScale, float& angularTimeScale);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const uint16_t springVersion;
|
static const uint16_t springVersion;
|
||||||
|
@ -46,6 +47,10 @@ protected:
|
||||||
glm::vec3 _linearVelocityTarget;
|
glm::vec3 _linearVelocityTarget;
|
||||||
glm::vec3 _angularVelocityTarget;
|
glm::vec3 _angularVelocityTarget;
|
||||||
|
|
||||||
|
EntityItemID _otherID;
|
||||||
|
SpatiallyNestableWeakPointer _other;
|
||||||
|
SpatiallyNestablePointer getOther();
|
||||||
|
|
||||||
virtual bool prepareForSpringUpdate(btScalar deltaTimeStep);
|
virtual bool prepareForSpringUpdate(btScalar deltaTimeStep);
|
||||||
|
|
||||||
void serializeParameters(QDataStream& dataStream) const;
|
void serializeParameters(QDataStream& dataStream) const;
|
||||||
|
|
240
libraries/physics/src/ObjectConstraintBallSocket.cpp
Normal file
240
libraries/physics/src/ObjectConstraintBallSocket.cpp
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
//
|
||||||
|
// ObjectConstraintBallSocket.cpp
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-29
|
||||||
|
// 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 "ObjectConstraintBallSocket.h"
|
||||||
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
|
|
||||||
|
const uint16_t ObjectConstraintBallSocket::constraintVersion = 1;
|
||||||
|
|
||||||
|
|
||||||
|
ObjectConstraintBallSocket::ObjectConstraintBallSocket(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||||
|
ObjectConstraint(DYNAMIC_TYPE_BALL_SOCKET, id, ownerEntity),
|
||||||
|
_pivotInA(glm::vec3(0.0f)),
|
||||||
|
_pivotInB(glm::vec3(0.0f))
|
||||||
|
{
|
||||||
|
#if WANT_DEBUG
|
||||||
|
qCDebug(physics) << "ObjectConstraintBallSocket::ObjectConstraintBallSocket";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectConstraintBallSocket::~ObjectConstraintBallSocket() {
|
||||||
|
#if WANT_DEBUG
|
||||||
|
qCDebug(physics) << "ObjectConstraintBallSocket::~ObjectConstraintBallSocket";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<btRigidBody*> ObjectConstraintBallSocket::getRigidBodies() {
|
||||||
|
QList<btRigidBody*> result;
|
||||||
|
result += getRigidBody();
|
||||||
|
QUuid otherEntityID;
|
||||||
|
withReadLock([&]{
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
});
|
||||||
|
if (!otherEntityID.isNull()) {
|
||||||
|
result += getOtherRigidBody(otherEntityID);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintBallSocket::prepareForPhysicsSimulation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintBallSocket::updateBallSocket() {
|
||||||
|
btPoint2PointConstraint* constraint { nullptr };
|
||||||
|
|
||||||
|
withReadLock([&]{
|
||||||
|
constraint = static_cast<btPoint2PointConstraint*>(_constraint);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!constraint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraint->setPivotA(glmToBullet(_pivotInA));
|
||||||
|
constraint->setPivotB(glmToBullet(_pivotInB));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
btTypedConstraint* ObjectConstraintBallSocket::getConstraint() {
|
||||||
|
btPoint2PointConstraint* constraint { nullptr };
|
||||||
|
QUuid otherEntityID;
|
||||||
|
glm::vec3 pivotInA;
|
||||||
|
glm::vec3 pivotInB;
|
||||||
|
|
||||||
|
withReadLock([&]{
|
||||||
|
constraint = static_cast<btPoint2PointConstraint*>(_constraint);
|
||||||
|
pivotInA = _pivotInA;
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
pivotInB = _pivotInB;
|
||||||
|
});
|
||||||
|
if (constraint) {
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
btRigidBody* rigidBodyA = getRigidBody();
|
||||||
|
if (!rigidBodyA) {
|
||||||
|
qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyA";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!otherEntityID.isNull()) {
|
||||||
|
// This constraint is between two entities... find the other rigid body.
|
||||||
|
|
||||||
|
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
|
||||||
|
if (!rigidBodyB) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraint = new btPoint2PointConstraint(*rigidBodyA, *rigidBodyB, glmToBullet(pivotInA), glmToBullet(pivotInB));
|
||||||
|
} else {
|
||||||
|
// This constraint is between an entity and the world-frame.
|
||||||
|
|
||||||
|
constraint = new btPoint2PointConstraint(*rigidBodyA, glmToBullet(pivotInA));
|
||||||
|
}
|
||||||
|
|
||||||
|
withWriteLock([&]{
|
||||||
|
_constraint = constraint;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we don't wake up rigidBodyA, we may not send the dynamicData property over the network
|
||||||
|
forceBodyNonStatic();
|
||||||
|
activateBody();
|
||||||
|
|
||||||
|
updateBallSocket();
|
||||||
|
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ObjectConstraintBallSocket::updateArguments(QVariantMap arguments) {
|
||||||
|
glm::vec3 pivotInA;
|
||||||
|
QUuid otherEntityID;
|
||||||
|
glm::vec3 pivotInB;
|
||||||
|
|
||||||
|
bool needUpdate = false;
|
||||||
|
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||||
|
withReadLock([&]{
|
||||||
|
bool ok = true;
|
||||||
|
pivotInA = EntityDynamicInterface::extractVec3Argument("ball-socket constraint", arguments, "pivot", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
pivotInA = _pivotInA;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("ball-socket constraint",
|
||||||
|
arguments, "otherEntityID", ok, false));
|
||||||
|
if (!ok) {
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
pivotInB = EntityDynamicInterface::extractVec3Argument("ball-socket constraint", arguments, "otherPivot", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
pivotInB = _pivotInB;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (somethingChanged ||
|
||||||
|
pivotInA != _pivotInA ||
|
||||||
|
otherEntityID != _otherEntityID ||
|
||||||
|
pivotInB != _pivotInB) {
|
||||||
|
// something changed
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (needUpdate) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_pivotInA = pivotInA;
|
||||||
|
_otherEntityID = otherEntityID;
|
||||||
|
_pivotInB = pivotInB;
|
||||||
|
|
||||||
|
_active = true;
|
||||||
|
|
||||||
|
auto ownerEntity = _ownerEntity.lock();
|
||||||
|
if (ownerEntity) {
|
||||||
|
ownerEntity->setDynamicDataDirty(true);
|
||||||
|
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateBallSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap ObjectConstraintBallSocket::getArguments() {
|
||||||
|
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||||
|
withReadLock([&] {
|
||||||
|
if (_constraint) {
|
||||||
|
arguments["pivot"] = glmToQMap(_pivotInA);
|
||||||
|
arguments["otherEntityID"] = _otherEntityID;
|
||||||
|
arguments["otherPivot"] = glmToQMap(_pivotInB);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ObjectConstraintBallSocket::serialize() const {
|
||||||
|
QByteArray serializedConstraintArguments;
|
||||||
|
QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
dataStream << DYNAMIC_TYPE_BALL_SOCKET;
|
||||||
|
dataStream << getID();
|
||||||
|
dataStream << ObjectConstraintBallSocket::constraintVersion;
|
||||||
|
|
||||||
|
withReadLock([&] {
|
||||||
|
dataStream << localTimeToServerTime(_expires);
|
||||||
|
dataStream << _tag;
|
||||||
|
|
||||||
|
dataStream << _pivotInA;
|
||||||
|
dataStream << _otherEntityID;
|
||||||
|
dataStream << _pivotInB;
|
||||||
|
});
|
||||||
|
|
||||||
|
return serializedConstraintArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintBallSocket::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 != ObjectConstraintBallSocket::constraintVersion) {
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
withWriteLock([&] {
|
||||||
|
quint64 serverExpires;
|
||||||
|
dataStream >> serverExpires;
|
||||||
|
_expires = serverTimeToLocalTime(serverExpires);
|
||||||
|
dataStream >> _tag;
|
||||||
|
|
||||||
|
dataStream >> _pivotInA;
|
||||||
|
dataStream >> _otherEntityID;
|
||||||
|
dataStream >> _pivotInB;
|
||||||
|
|
||||||
|
_active = true;
|
||||||
|
});
|
||||||
|
}
|
46
libraries/physics/src/ObjectConstraintBallSocket.h
Normal file
46
libraries/physics/src/ObjectConstraintBallSocket.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//
|
||||||
|
// ObjectConstraintBallSocket.h
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-29
|
||||||
|
// 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_ObjectConstraintBallSocket_h
|
||||||
|
#define hifi_ObjectConstraintBallSocket_h
|
||||||
|
|
||||||
|
#include "ObjectConstraint.h"
|
||||||
|
|
||||||
|
// http://bulletphysics.org/Bullet/BulletFull/classbtBallSocketConstraint.html
|
||||||
|
|
||||||
|
class ObjectConstraintBallSocket : public ObjectConstraint {
|
||||||
|
public:
|
||||||
|
ObjectConstraintBallSocket(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
|
virtual ~ObjectConstraintBallSocket();
|
||||||
|
|
||||||
|
virtual void prepareForPhysicsSimulation() override;
|
||||||
|
|
||||||
|
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 updateBallSocket();
|
||||||
|
|
||||||
|
glm::vec3 _pivotInA;
|
||||||
|
|
||||||
|
EntityItemID _otherEntityID;
|
||||||
|
glm::vec3 _pivotInB;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_ObjectConstraintBallSocket_h
|
367
libraries/physics/src/ObjectConstraintConeTwist.cpp
Normal file
367
libraries/physics/src/ObjectConstraintConeTwist.cpp
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
//
|
||||||
|
// ObjectConstraintConeTwist.cpp
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-29
|
||||||
|
// 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 "ObjectConstraintConeTwist.h"
|
||||||
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
|
|
||||||
|
const uint16_t ObjectConstraintConeTwist::constraintVersion = 1;
|
||||||
|
|
||||||
|
|
||||||
|
ObjectConstraintConeTwist::ObjectConstraintConeTwist(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||||
|
ObjectConstraint(DYNAMIC_TYPE_CONE_TWIST, id, ownerEntity),
|
||||||
|
_pivotInA(glm::vec3(0.0f)),
|
||||||
|
_axisInA(glm::vec3(0.0f))
|
||||||
|
{
|
||||||
|
#if WANT_DEBUG
|
||||||
|
qCDebug(physics) << "ObjectConstraintConeTwist::ObjectConstraintConeTwist";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectConstraintConeTwist::~ObjectConstraintConeTwist() {
|
||||||
|
#if WANT_DEBUG
|
||||||
|
qCDebug(physics) << "ObjectConstraintConeTwist::~ObjectConstraintConeTwist";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<btRigidBody*> ObjectConstraintConeTwist::getRigidBodies() {
|
||||||
|
QList<btRigidBody*> result;
|
||||||
|
result += getRigidBody();
|
||||||
|
QUuid otherEntityID;
|
||||||
|
withReadLock([&]{
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
});
|
||||||
|
if (!otherEntityID.isNull()) {
|
||||||
|
result += getOtherRigidBody(otherEntityID);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintConeTwist::prepareForPhysicsSimulation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintConeTwist::updateConeTwist() {
|
||||||
|
btConeTwistConstraint* constraint { nullptr };
|
||||||
|
float swingSpan1;
|
||||||
|
float swingSpan2;
|
||||||
|
float twistSpan;
|
||||||
|
float softness;
|
||||||
|
float biasFactor;
|
||||||
|
float relaxationFactor;
|
||||||
|
|
||||||
|
withReadLock([&]{
|
||||||
|
constraint = static_cast<btConeTwistConstraint*>(_constraint);
|
||||||
|
swingSpan1 = _swingSpan1;
|
||||||
|
swingSpan2 = _swingSpan2;
|
||||||
|
twistSpan = _twistSpan;
|
||||||
|
softness = _softness;
|
||||||
|
biasFactor = _biasFactor;
|
||||||
|
relaxationFactor = _relaxationFactor;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!constraint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraint->setLimit(swingSpan1,
|
||||||
|
swingSpan2,
|
||||||
|
twistSpan,
|
||||||
|
softness,
|
||||||
|
biasFactor,
|
||||||
|
relaxationFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
btTypedConstraint* ObjectConstraintConeTwist::getConstraint() {
|
||||||
|
btConeTwistConstraint* constraint { nullptr };
|
||||||
|
QUuid otherEntityID;
|
||||||
|
glm::vec3 pivotInA;
|
||||||
|
glm::vec3 axisInA;
|
||||||
|
glm::vec3 pivotInB;
|
||||||
|
glm::vec3 axisInB;
|
||||||
|
|
||||||
|
withReadLock([&]{
|
||||||
|
constraint = static_cast<btConeTwistConstraint*>(_constraint);
|
||||||
|
pivotInA = _pivotInA;
|
||||||
|
axisInA = _axisInA;
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
pivotInB = _pivotInB;
|
||||||
|
axisInB = _axisInB;
|
||||||
|
});
|
||||||
|
if (constraint) {
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
btRigidBody* rigidBodyA = getRigidBody();
|
||||||
|
if (!rigidBodyA) {
|
||||||
|
qCDebug(physics) << "ObjectConstraintConeTwist::getConstraint -- no rigidBodyA";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!otherEntityID.isNull()) {
|
||||||
|
// This coneTwist is between two entities... find the other rigid body.
|
||||||
|
|
||||||
|
glm::quat rotA = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA));
|
||||||
|
glm::quat rotB = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInB));
|
||||||
|
|
||||||
|
btTransform frameInA(glmToBullet(rotA), glmToBullet(pivotInA));
|
||||||
|
btTransform frameInB(glmToBullet(rotB), glmToBullet(pivotInB));
|
||||||
|
|
||||||
|
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
|
||||||
|
if (!rigidBodyB) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraint = new btConeTwistConstraint(*rigidBodyA, *rigidBodyB, frameInA, frameInB);
|
||||||
|
} else {
|
||||||
|
// This coneTwist is between an entity and the world-frame.
|
||||||
|
|
||||||
|
glm::quat rot = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA));
|
||||||
|
|
||||||
|
btTransform frameInA(glmToBullet(rot), glmToBullet(pivotInA));
|
||||||
|
|
||||||
|
constraint = new btConeTwistConstraint(*rigidBodyA, frameInA);
|
||||||
|
}
|
||||||
|
|
||||||
|
withWriteLock([&]{
|
||||||
|
_constraint = constraint;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we don't wake up rigidBodyA, we may not send the dynamicData property over the network
|
||||||
|
forceBodyNonStatic();
|
||||||
|
activateBody();
|
||||||
|
|
||||||
|
updateConeTwist();
|
||||||
|
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ObjectConstraintConeTwist::updateArguments(QVariantMap arguments) {
|
||||||
|
glm::vec3 pivotInA;
|
||||||
|
glm::vec3 axisInA;
|
||||||
|
QUuid otherEntityID;
|
||||||
|
glm::vec3 pivotInB;
|
||||||
|
glm::vec3 axisInB;
|
||||||
|
float swingSpan1;
|
||||||
|
float swingSpan2;
|
||||||
|
float twistSpan;
|
||||||
|
float softness;
|
||||||
|
float biasFactor;
|
||||||
|
float relaxationFactor;
|
||||||
|
|
||||||
|
bool needUpdate = false;
|
||||||
|
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||||
|
withReadLock([&]{
|
||||||
|
bool ok = true;
|
||||||
|
pivotInA = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "pivot", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
pivotInA = _pivotInA;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
axisInA = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "axis", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
axisInA = _axisInA;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("coneTwist constraint",
|
||||||
|
arguments, "otherEntityID", ok, false));
|
||||||
|
if (!ok) {
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
pivotInB = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "otherPivot", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
pivotInB = _pivotInB;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
axisInB = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "otherAxis", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
axisInB = _axisInB;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
swingSpan1 = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "swingSpan1", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
swingSpan1 = _swingSpan1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
swingSpan2 = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "swingSpan2", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
swingSpan2 = _swingSpan2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
twistSpan = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "twistSpan", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
twistSpan = _twistSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
softness = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "softness", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
softness = _softness;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
biasFactor = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "biasFactor", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
biasFactor = _biasFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
relaxationFactor =
|
||||||
|
EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "relaxationFactor", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
relaxationFactor = _relaxationFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (somethingChanged ||
|
||||||
|
pivotInA != _pivotInA ||
|
||||||
|
axisInA != _axisInA ||
|
||||||
|
otherEntityID != _otherEntityID ||
|
||||||
|
pivotInB != _pivotInB ||
|
||||||
|
axisInB != _axisInB ||
|
||||||
|
swingSpan1 != _swingSpan1 ||
|
||||||
|
swingSpan2 != _swingSpan2 ||
|
||||||
|
twistSpan != _twistSpan ||
|
||||||
|
softness != _softness ||
|
||||||
|
biasFactor != _biasFactor ||
|
||||||
|
relaxationFactor != _relaxationFactor) {
|
||||||
|
// something changed
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (needUpdate) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_pivotInA = pivotInA;
|
||||||
|
_axisInA = axisInA;
|
||||||
|
_otherEntityID = otherEntityID;
|
||||||
|
_pivotInB = pivotInB;
|
||||||
|
_axisInB = axisInB;
|
||||||
|
_swingSpan1 = swingSpan1;
|
||||||
|
_swingSpan2 = swingSpan2;
|
||||||
|
_twistSpan = twistSpan;
|
||||||
|
_softness = softness;
|
||||||
|
_biasFactor = biasFactor;
|
||||||
|
_relaxationFactor = relaxationFactor;
|
||||||
|
|
||||||
|
_active = true;
|
||||||
|
|
||||||
|
auto ownerEntity = _ownerEntity.lock();
|
||||||
|
if (ownerEntity) {
|
||||||
|
ownerEntity->setDynamicDataDirty(true);
|
||||||
|
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateConeTwist();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap ObjectConstraintConeTwist::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["swingSpan1"] = _swingSpan1;
|
||||||
|
arguments["swingSpan2"] = _swingSpan2;
|
||||||
|
arguments["twistSpan"] = _twistSpan;
|
||||||
|
arguments["softness"] = _softness;
|
||||||
|
arguments["biasFactor"] = _biasFactor;
|
||||||
|
arguments["relaxationFactor"] = _relaxationFactor;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ObjectConstraintConeTwist::serialize() const {
|
||||||
|
QByteArray serializedConstraintArguments;
|
||||||
|
QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
dataStream << DYNAMIC_TYPE_CONE_TWIST;
|
||||||
|
dataStream << getID();
|
||||||
|
dataStream << ObjectConstraintConeTwist::constraintVersion;
|
||||||
|
|
||||||
|
withReadLock([&] {
|
||||||
|
dataStream << localTimeToServerTime(_expires);
|
||||||
|
dataStream << _tag;
|
||||||
|
|
||||||
|
dataStream << _pivotInA;
|
||||||
|
dataStream << _axisInA;
|
||||||
|
dataStream << _otherEntityID;
|
||||||
|
dataStream << _pivotInB;
|
||||||
|
dataStream << _axisInB;
|
||||||
|
dataStream << _swingSpan1;
|
||||||
|
dataStream << _swingSpan2;
|
||||||
|
dataStream << _twistSpan;
|
||||||
|
dataStream << _softness;
|
||||||
|
dataStream << _biasFactor;
|
||||||
|
dataStream << _relaxationFactor;
|
||||||
|
});
|
||||||
|
|
||||||
|
return serializedConstraintArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintConeTwist::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 != ObjectConstraintConeTwist::constraintVersion) {
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
withWriteLock([&] {
|
||||||
|
quint64 serverExpires;
|
||||||
|
dataStream >> serverExpires;
|
||||||
|
_expires = serverTimeToLocalTime(serverExpires);
|
||||||
|
dataStream >> _tag;
|
||||||
|
|
||||||
|
dataStream >> _pivotInA;
|
||||||
|
dataStream >> _axisInA;
|
||||||
|
dataStream >> _otherEntityID;
|
||||||
|
dataStream >> _pivotInB;
|
||||||
|
dataStream >> _axisInB;
|
||||||
|
dataStream >> _swingSpan1;
|
||||||
|
dataStream >> _swingSpan2;
|
||||||
|
dataStream >> _twistSpan;
|
||||||
|
dataStream >> _softness;
|
||||||
|
dataStream >> _biasFactor;
|
||||||
|
dataStream >> _relaxationFactor;
|
||||||
|
|
||||||
|
_active = true;
|
||||||
|
});
|
||||||
|
}
|
55
libraries/physics/src/ObjectConstraintConeTwist.h
Normal file
55
libraries/physics/src/ObjectConstraintConeTwist.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// ObjectConstraintConeTwist.h
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-23
|
||||||
|
// 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_ObjectConstraintConeTwist_h
|
||||||
|
#define hifi_ObjectConstraintConeTwist_h
|
||||||
|
|
||||||
|
#include "ObjectConstraint.h"
|
||||||
|
|
||||||
|
// http://bulletphysics.org/Bullet/BulletFull/classbtConeTwistConstraint.html
|
||||||
|
|
||||||
|
class ObjectConstraintConeTwist : public ObjectConstraint {
|
||||||
|
public:
|
||||||
|
ObjectConstraintConeTwist(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
|
virtual ~ObjectConstraintConeTwist();
|
||||||
|
|
||||||
|
virtual void prepareForPhysicsSimulation() override;
|
||||||
|
|
||||||
|
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 updateConeTwist();
|
||||||
|
|
||||||
|
glm::vec3 _pivotInA;
|
||||||
|
glm::vec3 _axisInA;
|
||||||
|
|
||||||
|
EntityItemID _otherEntityID;
|
||||||
|
glm::vec3 _pivotInB;
|
||||||
|
glm::vec3 _axisInB;
|
||||||
|
|
||||||
|
float _swingSpan1 { TWO_PI };
|
||||||
|
float _swingSpan2 { TWO_PI };;
|
||||||
|
float _twistSpan { TWO_PI };;
|
||||||
|
float _softness { 1.0f };
|
||||||
|
float _biasFactor {0.3f };
|
||||||
|
float _relaxationFactor { 1.0f };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_ObjectConstraintConeTwist_h
|
|
@ -48,23 +48,26 @@ QList<btRigidBody*> ObjectConstraintHinge::getRigidBodies() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintHinge::prepareForPhysicsSimulation() {
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectConstraintHinge::updateHinge() {
|
void ObjectConstraintHinge::updateHinge() {
|
||||||
btHingeConstraint* constraint { nullptr };
|
btHingeConstraint* constraint { nullptr };
|
||||||
|
glm::vec3 axisInA;
|
||||||
float low;
|
float low;
|
||||||
float high;
|
float high;
|
||||||
float softness;
|
float softness;
|
||||||
float biasFactor;
|
float biasFactor;
|
||||||
float relaxationFactor;
|
float relaxationFactor;
|
||||||
float motorVelocity;
|
|
||||||
|
|
||||||
withReadLock([&]{
|
withReadLock([&]{
|
||||||
|
axisInA = _axisInA;
|
||||||
constraint = static_cast<btHingeConstraint*>(_constraint);
|
constraint = static_cast<btHingeConstraint*>(_constraint);
|
||||||
low = _low;
|
low = _low;
|
||||||
high = _high;
|
high = _high;
|
||||||
softness = _softness;
|
|
||||||
biasFactor = _biasFactor;
|
biasFactor = _biasFactor;
|
||||||
relaxationFactor = _relaxationFactor;
|
relaxationFactor = _relaxationFactor;
|
||||||
motorVelocity = _motorVelocity;
|
softness = _softness;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!constraint) {
|
if (!constraint) {
|
||||||
|
@ -72,12 +75,6 @@ void ObjectConstraintHinge::updateHinge() {
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint->setLimit(low, high, softness, biasFactor, relaxationFactor);
|
constraint->setLimit(low, high, softness, biasFactor, relaxationFactor);
|
||||||
if (motorVelocity != 0.0f) {
|
|
||||||
constraint->setMotorTargetVelocity(motorVelocity);
|
|
||||||
constraint->enableMotor(true);
|
|
||||||
} else {
|
|
||||||
constraint->enableMotor(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,7 +147,6 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) {
|
||||||
float softness;
|
float softness;
|
||||||
float biasFactor;
|
float biasFactor;
|
||||||
float relaxationFactor;
|
float relaxationFactor;
|
||||||
float motorVelocity;
|
|
||||||
|
|
||||||
bool needUpdate = false;
|
bool needUpdate = false;
|
||||||
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||||
|
@ -217,13 +213,6 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) {
|
||||||
relaxationFactor = _relaxationFactor;
|
relaxationFactor = _relaxationFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = true;
|
|
||||||
motorVelocity = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments,
|
|
||||||
"motorVelocity", ok, false);
|
|
||||||
if (!ok) {
|
|
||||||
motorVelocity = _motorVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (somethingChanged ||
|
if (somethingChanged ||
|
||||||
pivotInA != _pivotInA ||
|
pivotInA != _pivotInA ||
|
||||||
axisInA != _axisInA ||
|
axisInA != _axisInA ||
|
||||||
|
@ -234,8 +223,7 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) {
|
||||||
high != _high ||
|
high != _high ||
|
||||||
softness != _softness ||
|
softness != _softness ||
|
||||||
biasFactor != _biasFactor ||
|
biasFactor != _biasFactor ||
|
||||||
relaxationFactor != _relaxationFactor ||
|
relaxationFactor != _relaxationFactor) {
|
||||||
motorVelocity != _motorVelocity) {
|
|
||||||
// something changed
|
// something changed
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
|
@ -253,7 +241,6 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) {
|
||||||
_softness = softness;
|
_softness = softness;
|
||||||
_biasFactor = biasFactor;
|
_biasFactor = biasFactor;
|
||||||
_relaxationFactor = relaxationFactor;
|
_relaxationFactor = relaxationFactor;
|
||||||
_motorVelocity = motorVelocity;
|
|
||||||
|
|
||||||
_active = true;
|
_active = true;
|
||||||
|
|
||||||
|
@ -284,7 +271,6 @@ QVariantMap ObjectConstraintHinge::getArguments() {
|
||||||
arguments["softness"] = _softness;
|
arguments["softness"] = _softness;
|
||||||
arguments["biasFactor"] = _biasFactor;
|
arguments["biasFactor"] = _biasFactor;
|
||||||
arguments["relaxationFactor"] = _relaxationFactor;
|
arguments["relaxationFactor"] = _relaxationFactor;
|
||||||
arguments["motorVelocity"] = _motorVelocity;
|
|
||||||
arguments["angle"] = static_cast<btHingeConstraint*>(_constraint)->getHingeAngle(); // [-PI,PI]
|
arguments["angle"] = static_cast<btHingeConstraint*>(_constraint)->getHingeAngle(); // [-PI,PI]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -313,8 +299,6 @@ QByteArray ObjectConstraintHinge::serialize() const {
|
||||||
|
|
||||||
dataStream << localTimeToServerTime(_expires);
|
dataStream << localTimeToServerTime(_expires);
|
||||||
dataStream << _tag;
|
dataStream << _tag;
|
||||||
|
|
||||||
dataStream << _motorVelocity;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return serializedConstraintArguments;
|
return serializedConstraintArguments;
|
||||||
|
@ -356,8 +340,6 @@ void ObjectConstraintHinge::deserialize(QByteArray serializedArguments) {
|
||||||
|
|
||||||
dataStream >> _tag;
|
dataStream >> _tag;
|
||||||
|
|
||||||
dataStream >> _motorVelocity;
|
|
||||||
|
|
||||||
_active = true;
|
_active = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ public:
|
||||||
ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity);
|
ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
virtual ~ObjectConstraintHinge();
|
virtual ~ObjectConstraintHinge();
|
||||||
|
|
||||||
|
virtual void prepareForPhysicsSimulation() override;
|
||||||
|
|
||||||
virtual bool updateArguments(QVariantMap arguments) override;
|
virtual bool updateArguments(QVariantMap arguments) override;
|
||||||
virtual QVariantMap getArguments() override;
|
virtual QVariantMap getArguments() override;
|
||||||
|
|
||||||
|
@ -42,12 +44,32 @@ protected:
|
||||||
glm::vec3 _pivotInB;
|
glm::vec3 _pivotInB;
|
||||||
glm::vec3 _axisInB;
|
glm::vec3 _axisInB;
|
||||||
|
|
||||||
float _low { -2.0f * PI };
|
float _low { -TWO_PI };
|
||||||
float _high { 2.0f * PI };
|
float _high { TWO_PI };
|
||||||
|
|
||||||
|
// https://gamedev.stackexchange.com/questions/71436/what-are-the-parameters-for-bthingeconstraintsetlimit
|
||||||
|
//
|
||||||
|
// softness: a negative measure of the friction that determines how much the hinge rotates for a given force. A high
|
||||||
|
// softness would make the hinge rotate easily like it's oiled then.
|
||||||
|
// biasFactor: an offset for the relaxed rotation of the hinge. It won't be right in the middle of the low and high angles
|
||||||
|
// anymore. 1.0f is the neural value.
|
||||||
|
// relaxationFactor: a measure of how much force is applied internally to bring the hinge in its central rotation.
|
||||||
|
// This is right in the middle of the low and high angles. For example, consider a western swing door. After
|
||||||
|
// walking through it will swing in both directions but at the end it stays right in the middle.
|
||||||
|
|
||||||
|
// http://javadoc.jmonkeyengine.org/com/jme3/bullet/joints/HingeJoint.html
|
||||||
|
//
|
||||||
|
// _softness - the factor at which the velocity error correction starts operating, i.e. a softness of 0.9 means that
|
||||||
|
// the vel. corr starts at 90% of the limit range.
|
||||||
|
// _biasFactor - the magnitude of the position correction. It tells you how strictly the position error (drift) is
|
||||||
|
// corrected.
|
||||||
|
// _relaxationFactor - the rate at which velocity errors are corrected. This can be seen as the strength of the
|
||||||
|
// limits. A low value will make the the limits more spongy.
|
||||||
|
|
||||||
|
|
||||||
float _softness { 0.9f };
|
float _softness { 0.9f };
|
||||||
float _biasFactor { 0.3f };
|
float _biasFactor { 0.3f };
|
||||||
float _relaxationFactor { 1.0f };
|
float _relaxationFactor { 1.0f };
|
||||||
float _motorVelocity { 0.0f };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ObjectConstraintHinge_h
|
#endif // hifi_ObjectConstraintHinge_h
|
||||||
|
|
326
libraries/physics/src/ObjectConstraintSlider.cpp
Normal file
326
libraries/physics/src/ObjectConstraintSlider.cpp
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
//
|
||||||
|
// ObjectConstraintSlider.cpp
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-23
|
||||||
|
// 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 "ObjectConstraintSlider.h"
|
||||||
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
|
|
||||||
|
const uint16_t ObjectConstraintSlider::constraintVersion = 1;
|
||||||
|
|
||||||
|
|
||||||
|
ObjectConstraintSlider::ObjectConstraintSlider(const QUuid& id, EntityItemPointer ownerEntity) :
|
||||||
|
ObjectConstraint(DYNAMIC_TYPE_SLIDER, id, ownerEntity),
|
||||||
|
_pointInA(glm::vec3(0.0f)),
|
||||||
|
_axisInA(glm::vec3(0.0f))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectConstraintSlider::~ObjectConstraintSlider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<btRigidBody*> ObjectConstraintSlider::getRigidBodies() {
|
||||||
|
QList<btRigidBody*> result;
|
||||||
|
result += getRigidBody();
|
||||||
|
QUuid otherEntityID;
|
||||||
|
withReadLock([&]{
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
});
|
||||||
|
if (!otherEntityID.isNull()) {
|
||||||
|
result += getOtherRigidBody(otherEntityID);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintSlider::prepareForPhysicsSimulation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintSlider::updateSlider() {
|
||||||
|
btSliderConstraint* constraint { nullptr };
|
||||||
|
|
||||||
|
withReadLock([&]{
|
||||||
|
constraint = static_cast<btSliderConstraint*>(_constraint);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!constraint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// constraint->setFrames (const btTransform &frameA, const btTransform &frameB);
|
||||||
|
constraint->setLowerLinLimit(_linearLow);
|
||||||
|
constraint->setUpperLinLimit(_linearHigh);
|
||||||
|
constraint->setLowerAngLimit(_angularLow);
|
||||||
|
constraint->setUpperAngLimit(_angularHigh);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
btTypedConstraint* ObjectConstraintSlider::getConstraint() {
|
||||||
|
btSliderConstraint* constraint { nullptr };
|
||||||
|
QUuid otherEntityID;
|
||||||
|
glm::vec3 pointInA;
|
||||||
|
glm::vec3 axisInA;
|
||||||
|
glm::vec3 pointInB;
|
||||||
|
glm::vec3 axisInB;
|
||||||
|
|
||||||
|
withReadLock([&]{
|
||||||
|
constraint = static_cast<btSliderConstraint*>(_constraint);
|
||||||
|
pointInA = _pointInA;
|
||||||
|
axisInA = _axisInA;
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
pointInB = _pointInB;
|
||||||
|
axisInB = _axisInB;
|
||||||
|
});
|
||||||
|
if (constraint) {
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
btRigidBody* rigidBodyA = getRigidBody();
|
||||||
|
if (!rigidBodyA) {
|
||||||
|
qCDebug(physics) << "ObjectConstraintSlider::getConstraint -- no rigidBodyA";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!otherEntityID.isNull()) {
|
||||||
|
// This slider is between two entities... find the other rigid body.
|
||||||
|
|
||||||
|
glm::quat rotA = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA));
|
||||||
|
glm::quat rotB = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInB));
|
||||||
|
|
||||||
|
btTransform frameInA(glmToBullet(rotA), glmToBullet(pointInA));
|
||||||
|
btTransform frameInB(glmToBullet(rotB), glmToBullet(pointInB));
|
||||||
|
|
||||||
|
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
|
||||||
|
if (!rigidBodyB) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraint = new btSliderConstraint(*rigidBodyA, *rigidBodyB, frameInA, frameInB, true);
|
||||||
|
} else {
|
||||||
|
// This slider is between an entity and the world-frame.
|
||||||
|
|
||||||
|
glm::quat rot = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA));
|
||||||
|
|
||||||
|
btTransform frameInA(glmToBullet(rot), glmToBullet(pointInA));
|
||||||
|
|
||||||
|
constraint = new btSliderConstraint(*rigidBodyA, frameInA, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
withWriteLock([&]{
|
||||||
|
_constraint = constraint;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we don't wake up rigidBodyA, we may not send the dynamicData property over the network
|
||||||
|
forceBodyNonStatic();
|
||||||
|
activateBody();
|
||||||
|
|
||||||
|
updateSlider();
|
||||||
|
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) {
|
||||||
|
glm::vec3 pointInA;
|
||||||
|
glm::vec3 axisInA;
|
||||||
|
QUuid otherEntityID;
|
||||||
|
glm::vec3 pointInB;
|
||||||
|
glm::vec3 axisInB;
|
||||||
|
float linearLow;
|
||||||
|
float linearHigh;
|
||||||
|
float angularLow;
|
||||||
|
float angularHigh;
|
||||||
|
|
||||||
|
bool needUpdate = false;
|
||||||
|
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
|
||||||
|
withReadLock([&]{
|
||||||
|
bool ok = true;
|
||||||
|
pointInA = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "point", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
pointInA = _pointInA;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
axisInA = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "axis", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
axisInA = _axisInA;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("slider constraint",
|
||||||
|
arguments, "otherEntityID", ok, false));
|
||||||
|
if (!ok) {
|
||||||
|
otherEntityID = _otherEntityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
pointInB = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "otherPoint", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
pointInB = _pointInB;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
axisInB = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "otherAxis", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
axisInB = _axisInB;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
linearLow = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "linearLow", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
linearLow = _linearLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
linearHigh = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "linearHigh", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
linearHigh = _linearHigh;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
angularLow = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "angularLow", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
angularLow = _angularLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
angularHigh = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "angularHigh", ok, false);
|
||||||
|
if (!ok) {
|
||||||
|
angularHigh = _angularHigh;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (somethingChanged ||
|
||||||
|
pointInA != _pointInA ||
|
||||||
|
axisInA != _axisInA ||
|
||||||
|
otherEntityID != _otherEntityID ||
|
||||||
|
pointInB != _pointInB ||
|
||||||
|
axisInB != _axisInB ||
|
||||||
|
linearLow != _linearLow ||
|
||||||
|
linearHigh != _linearHigh ||
|
||||||
|
angularLow != _angularLow ||
|
||||||
|
angularHigh != _angularHigh) {
|
||||||
|
// something changed
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (needUpdate) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_pointInA = pointInA;
|
||||||
|
_axisInA = axisInA;
|
||||||
|
_otherEntityID = otherEntityID;
|
||||||
|
_pointInB = pointInB;
|
||||||
|
_axisInB = axisInB;
|
||||||
|
_linearLow = linearLow;
|
||||||
|
_linearHigh = linearHigh;
|
||||||
|
_angularLow = angularLow;
|
||||||
|
_angularHigh = angularHigh;
|
||||||
|
|
||||||
|
_active = true;
|
||||||
|
|
||||||
|
auto ownerEntity = _ownerEntity.lock();
|
||||||
|
if (ownerEntity) {
|
||||||
|
ownerEntity->setDynamicDataDirty(true);
|
||||||
|
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSlider();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap ObjectConstraintSlider::getArguments() {
|
||||||
|
QVariantMap arguments = ObjectDynamic::getArguments();
|
||||||
|
withReadLock([&] {
|
||||||
|
if (_constraint) {
|
||||||
|
arguments["point"] = glmToQMap(_pointInA);
|
||||||
|
arguments["axis"] = glmToQMap(_axisInA);
|
||||||
|
arguments["otherEntityID"] = _otherEntityID;
|
||||||
|
arguments["otherPoint"] = glmToQMap(_pointInB);
|
||||||
|
arguments["otherAxis"] = glmToQMap(_axisInB);
|
||||||
|
arguments["linearLow"] = _linearLow;
|
||||||
|
arguments["linearHigh"] = _linearHigh;
|
||||||
|
arguments["angularLow"] = _angularLow;
|
||||||
|
arguments["angularHigh"] = _angularHigh;
|
||||||
|
arguments["linearPosition"] = static_cast<btSliderConstraint*>(_constraint)->getLinearPos();
|
||||||
|
arguments["angularPosition"] = static_cast<btSliderConstraint*>(_constraint)->getAngularPos();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ObjectConstraintSlider::serialize() const {
|
||||||
|
QByteArray serializedConstraintArguments;
|
||||||
|
QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
dataStream << DYNAMIC_TYPE_SLIDER;
|
||||||
|
dataStream << getID();
|
||||||
|
dataStream << ObjectConstraintSlider::constraintVersion;
|
||||||
|
|
||||||
|
withReadLock([&] {
|
||||||
|
dataStream << localTimeToServerTime(_expires);
|
||||||
|
dataStream << _tag;
|
||||||
|
|
||||||
|
dataStream << _pointInA;
|
||||||
|
dataStream << _axisInA;
|
||||||
|
dataStream << _otherEntityID;
|
||||||
|
dataStream << _pointInB;
|
||||||
|
dataStream << _axisInB;
|
||||||
|
dataStream << _linearLow;
|
||||||
|
dataStream << _linearHigh;
|
||||||
|
dataStream << _angularLow;
|
||||||
|
dataStream << _angularHigh;
|
||||||
|
});
|
||||||
|
|
||||||
|
return serializedConstraintArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectConstraintSlider::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 != ObjectConstraintSlider::constraintVersion) {
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
withWriteLock([&] {
|
||||||
|
quint64 serverExpires;
|
||||||
|
dataStream >> serverExpires;
|
||||||
|
_expires = serverTimeToLocalTime(serverExpires);
|
||||||
|
dataStream >> _tag;
|
||||||
|
|
||||||
|
dataStream >> _pointInA;
|
||||||
|
dataStream >> _axisInA;
|
||||||
|
dataStream >> _otherEntityID;
|
||||||
|
dataStream >> _pointInB;
|
||||||
|
dataStream >> _axisInB;
|
||||||
|
dataStream >> _linearLow;
|
||||||
|
dataStream >> _linearHigh;
|
||||||
|
dataStream >> _angularLow;
|
||||||
|
dataStream >> _angularHigh;
|
||||||
|
|
||||||
|
_active = true;
|
||||||
|
});
|
||||||
|
}
|
54
libraries/physics/src/ObjectConstraintSlider.h
Normal file
54
libraries/physics/src/ObjectConstraintSlider.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// ObjectConstraintSlider.h
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-23
|
||||||
|
// 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_ObjectConstraintSlider_h
|
||||||
|
#define hifi_ObjectConstraintSlider_h
|
||||||
|
|
||||||
|
#include "ObjectConstraint.h"
|
||||||
|
|
||||||
|
// http://bulletphysics.org/Bullet/BulletFull/classbtSliderConstraint.html
|
||||||
|
|
||||||
|
class ObjectConstraintSlider : public ObjectConstraint {
|
||||||
|
public:
|
||||||
|
ObjectConstraintSlider(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
|
virtual ~ObjectConstraintSlider();
|
||||||
|
|
||||||
|
virtual void prepareForPhysicsSimulation() override;
|
||||||
|
|
||||||
|
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 updateSlider();
|
||||||
|
|
||||||
|
glm::vec3 _pointInA;
|
||||||
|
glm::vec3 _axisInA;
|
||||||
|
|
||||||
|
EntityItemID _otherEntityID;
|
||||||
|
glm::vec3 _pointInB;
|
||||||
|
glm::vec3 _axisInB;
|
||||||
|
|
||||||
|
float _linearLow { std::numeric_limits<float>::max() };
|
||||||
|
float _linearHigh { std::numeric_limits<float>::min() };
|
||||||
|
|
||||||
|
float _angularLow { -TWO_PI };
|
||||||
|
float _angularHigh { TWO_PI };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_ObjectConstraintSlider_h
|
76
scripts/developer/tests/dynamics/dynamics-tests-interface.js
Normal file
76
scripts/developer/tests/dynamics/dynamics-tests-interface.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// dynamics-tests-interface.js
|
||||||
|
// scripts/developer/tests/dynamics/
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-30
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
/* globals $, EventBridge */
|
||||||
|
|
||||||
|
var parameters = {
|
||||||
|
"lifetime":"integer"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function getQueryArgByName(name, url) {
|
||||||
|
if (!url) {
|
||||||
|
url = window.location.href;
|
||||||
|
}
|
||||||
|
name = name.replace(/[\[\]]/g, "\\$&");
|
||||||
|
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
|
||||||
|
results = regex.exec(url);
|
||||||
|
if (!results) return null;
|
||||||
|
if (!results[2]) return '';
|
||||||
|
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addCommandParameters(params) {
|
||||||
|
// copy from html elements into an associative-array which will get passed (as JSON) through the EventBridge
|
||||||
|
for (var parameterName in parameters) {
|
||||||
|
if (parameters.hasOwnProperty(parameterName)) {
|
||||||
|
var parameterType = parameters[parameterName];
|
||||||
|
var strVal = $("#" + parameterName).val();
|
||||||
|
if (parameterType == "integer") {
|
||||||
|
params[parameterName] = parseInt(strVal);
|
||||||
|
} else if (parameterType == "float") {
|
||||||
|
params[parameterName] = parseFloat(strVal);
|
||||||
|
} else {
|
||||||
|
params[parameterName] = strVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
// hook all buttons to EventBridge
|
||||||
|
$(":button").each(function(index) {
|
||||||
|
$(this).click(function() {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify(addCommandParameters({ "dynamics-tests-command": this.id })));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// copy parameters from query-args into elements
|
||||||
|
for (var parameterName in parameters) {
|
||||||
|
if (parameters.hasOwnProperty(parameterName)) {
|
||||||
|
var val = getQueryArgByName(parameterName);
|
||||||
|
if (val) {
|
||||||
|
var parameterType = parameters[parameterName];
|
||||||
|
if (parameterType == "integer") {
|
||||||
|
val = parseInt(val);
|
||||||
|
} else if (parameterType == "float") {
|
||||||
|
val = parseFloat(val);
|
||||||
|
}
|
||||||
|
$("#" + parameterName).val(val.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
37
scripts/developer/tests/dynamics/dynamics-tests.html
Normal file
37
scripts/developer/tests/dynamics/dynamics-tests.html
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Dynamics Tests</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||||
|
<script src="dynamics-tests-interface.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
lifetime: <input id="lifetime" type="text" size=6>
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="cone-twist-and-spring-lever-test" value="Cone-Twist and Spring-Action Lever"><br>
|
||||||
|
A platform with a lever. The lever can be moved in a cone and rotated. A spring brings it back to its neutral position.
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="door-vs-world-test" value="Hinge Between Swinging Door and World"><br>
|
||||||
|
A grabbable door with a hinge between it and world-space.
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="hinge-chain-test" value="Hinge Chain"><br>
|
||||||
|
A chain of blocks connected by hinges.
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="slider-vs-world-test" value="Slider vs World"><br>
|
||||||
|
The block can only move up and down over a range of 1/2 meter.
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="slider-chain-test" value="Slider Chain"><br>
|
||||||
|
A chain of blocks connected by slider constraints.
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="ball-socket-between-test" value="Ball-Socket Between Spheres Chain"><br>
|
||||||
|
A chain of spheres connected by ball-and-socket joints between the spheres.
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="ball-socket-coincident-test" value="Ball-Socket Coincident Spheres Chain"><br>
|
||||||
|
A chain of spheres connected by ball-and-socket joints coincident-with the spheres.
|
||||||
|
<hr>
|
||||||
|
<input type="button" id="ragdoll-test" value="Ragdoll"><br>
|
||||||
|
A self-righting ragdoll. The head is on a weak spring vs the body.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
759
scripts/developer/tests/dynamics/dynamicsTests.js
Normal file
759
scripts/developer/tests/dynamics/dynamicsTests.js
Normal file
|
@ -0,0 +1,759 @@
|
||||||
|
//
|
||||||
|
// dynamicsTests.js
|
||||||
|
// scripts/developer/tests/dynamics/
|
||||||
|
//
|
||||||
|
// Created by Seth Alves 2017-4-30
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* global Entities, Script, Tablet, MyAvatar, Vec3 */
|
||||||
|
|
||||||
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
var DYNAMICS_TESTS_URL = Script.resolvePath("dynamics-tests.html");
|
||||||
|
var DEFAULT_LIFETIME = 120; // seconds
|
||||||
|
|
||||||
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
|
||||||
|
var button = tablet.addButton({
|
||||||
|
icon: Script.resolvePath("dynamicsTests.svg"),
|
||||||
|
text: "Dynamics",
|
||||||
|
sortOrder: 15
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function coneTwistAndSpringLeverTest(params) {
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: -0.5, z: -2}));
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
|
||||||
|
var baseID = Entities.addEntity({
|
||||||
|
name: "cone-twist test -- base",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: 0.5, y: 0.2, z: 0.5 },
|
||||||
|
position: Vec3.sum(pos, { x: 0, y: 0, z:0 }),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
var leverID = Entities.addEntity({
|
||||||
|
name: "cone-twist test -- lever",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 200 },
|
||||||
|
dimensions: { x: 0.05, y: 1, z: 0.05 },
|
||||||
|
position: Vec3.sum(pos, { x: 0, y: 0.7, z:0 }),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addEntity({
|
||||||
|
name: "cone-twist test -- handle",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 30, green: 100, red: 200 },
|
||||||
|
dimensions: { x: 0.1, y: 0.08, z: 0.08 },
|
||||||
|
position: Vec3.sum(pos, { x: 0, y: 0.7 + 0.5, z:0 }),
|
||||||
|
dynamic: false,
|
||||||
|
collisionless: true,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
parentID: leverID,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("cone-twist", baseID, {
|
||||||
|
pivot: { x: 0, y: 0.2, z: 0 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
otherEntityID: leverID,
|
||||||
|
otherPivot: { x: 0, y: -0.55, z: 0 },
|
||||||
|
otherAxis: { x: 0, y: 1, z: 0 },
|
||||||
|
swingSpan1: Math.PI / 4,
|
||||||
|
swingSpan2: Math.PI / 4,
|
||||||
|
twistSpan: Math.PI / 2,
|
||||||
|
tag: "cone-twist test"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("spring", leverID, {
|
||||||
|
targetRotation: { x: 0, y: 0, z: 0, w: 1 },
|
||||||
|
angularTimeScale: 0.2,
|
||||||
|
tag: "cone-twist test spring"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Entities.editEntity(baseID, { gravity: { x: 0, y: -5, z: 0 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
function doorVSWorldTest(params) {
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2}));
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
|
||||||
|
var doorID = Entities.addEntity({
|
||||||
|
name: "door test",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 20, red: 20 },
|
||||||
|
dimensions: { x: 1.0, y: 2, z: 0.1 },
|
||||||
|
position: pos,
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
lifetime: lifetime,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("hinge", doorID, {
|
||||||
|
pivot: { x: -0.5, y: 0, z: 0 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
low: 0,
|
||||||
|
high: Math.PI,
|
||||||
|
tag: "door hinge test"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hingeChainTest(params) {
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2}));
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
|
||||||
|
var offset = 0.28;
|
||||||
|
var prevEntityID = null;
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
var newID = Entities.addEntity({
|
||||||
|
name: "hinge test " + i,
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 40 * i, red: 20 },
|
||||||
|
dimensions: { x: 0.2, y: 0.2, z: 0.1 },
|
||||||
|
position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
lifetime: lifetime,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
if (prevEntityID) {
|
||||||
|
Entities.addAction("hinge", prevEntityID, {
|
||||||
|
pivot: { x: 0, y: offset / 2, z: 0 },
|
||||||
|
axis: { x: 1, y: 0, z: 0 },
|
||||||
|
otherEntityID: newID,
|
||||||
|
otherPivot: { x: 0, y: -offset / 2, z: 0 },
|
||||||
|
otherAxis: { x: 1, y: 0, z: 0 },
|
||||||
|
tag: "A/B hinge test " + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prevEntityID = newID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sliderVSWorldTest(params) {
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2}));
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
|
||||||
|
var sliderEntityID = Entities.addEntity({
|
||||||
|
name: "slider test",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 20, red: 20 },
|
||||||
|
dimensions: { x: 0.2, y: 0.2, z: 0.2 },
|
||||||
|
position: pos,
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("slider", sliderEntityID, {
|
||||||
|
point: { x: -0.5, y: 0, z: 0 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
linearLow: 0,
|
||||||
|
linearHigh: 0.6,
|
||||||
|
tag: "slider test"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sliderChainTest(params) {
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2}));
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
|
||||||
|
var offset = 0.28;
|
||||||
|
var prevEntityID = null;
|
||||||
|
for (var i = 0; i < 7; i++) {
|
||||||
|
var newID = Entities.addEntity({
|
||||||
|
name: "hinge test " + i,
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 40 * i, red: 20 },
|
||||||
|
dimensions: { x: 0.2, y: 0.1, z: 0.2 },
|
||||||
|
position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
if (prevEntityID) {
|
||||||
|
Entities.addAction("slider", prevEntityID, {
|
||||||
|
point: { x: 0, y: 0, z: 0 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
otherEntityID: newID,
|
||||||
|
otherPoint: { x: 0, y: -offset / 2, z: 0 },
|
||||||
|
otherAxis: { x: 0, y: 1, z: 0 },
|
||||||
|
linearLow: 0,
|
||||||
|
linearHigh: 0.6,
|
||||||
|
tag: "A/B slider test " + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prevEntityID = newID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ballSocketBetweenTest(params) {
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2}));
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
|
||||||
|
var offset = 0.2;
|
||||||
|
var diameter = offset - 0.01;
|
||||||
|
var prevEntityID = null;
|
||||||
|
for (var i = 0; i < 7; i++) {
|
||||||
|
var newID = Entities.addEntity({
|
||||||
|
name: "ball and socket test " + i,
|
||||||
|
type: "Sphere",
|
||||||
|
color: { blue: 128, green: 40 * i, red: 20 },
|
||||||
|
dimensions: { x: diameter, y: diameter, z: diameter },
|
||||||
|
position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
lifetime: lifetime,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
if (prevEntityID) {
|
||||||
|
Entities.addAction("ball-socket", prevEntityID, {
|
||||||
|
pivot: { x: 0, y: offset / 2, z: 0 },
|
||||||
|
otherEntityID: newID,
|
||||||
|
otherPivot: { x: 0, y: -offset / 2, z: 0 },
|
||||||
|
tag: "A/B ball-and-socket test " + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prevEntityID = newID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ballSocketCoincidentTest(params) {
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2}));
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
|
||||||
|
var offset = 0.2;
|
||||||
|
var diameter = offset - 0.01;
|
||||||
|
var prevEntityID = null;
|
||||||
|
for (var i = 0; i < 7; i++) {
|
||||||
|
var newID = Entities.addEntity({
|
||||||
|
name: "ball and socket test " + i,
|
||||||
|
type: "Sphere",
|
||||||
|
color: { blue: 128, green: 40 * i, red: 20 },
|
||||||
|
dimensions: { x: diameter, y: diameter, z: diameter },
|
||||||
|
position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
lifetime: lifetime,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
if (prevEntityID) {
|
||||||
|
Entities.addAction("ball-socket", prevEntityID, {
|
||||||
|
pivot: { x: 0, y: 0, z: 0 },
|
||||||
|
otherEntityID: newID,
|
||||||
|
otherPivot: { x: 0, y: offset, z: 0 },
|
||||||
|
tag: "A/B ball-and-socket test " + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prevEntityID = newID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ragdollTest(params) {
|
||||||
|
var scale = 1.6;
|
||||||
|
var lifetime = params.lifetime;
|
||||||
|
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 1.0, z: -2}));
|
||||||
|
|
||||||
|
var neckLength = scale * 0.05;
|
||||||
|
var shoulderGap = scale * 0.1;
|
||||||
|
var elbowGap = scale * 0.06;
|
||||||
|
var hipGap = scale * 0.07;
|
||||||
|
var kneeGap = scale * 0.08;
|
||||||
|
var ankleGap = scale * 0.06;
|
||||||
|
var ankleMin = 0;
|
||||||
|
var ankleMax = Math.PI / 4;
|
||||||
|
|
||||||
|
var headSize = scale * 0.2;
|
||||||
|
|
||||||
|
var bodyHeight = scale * 0.4;
|
||||||
|
var bodyWidth = scale * 0.3;
|
||||||
|
var bodyDepth = scale * 0.2;
|
||||||
|
|
||||||
|
var upperArmThickness = scale * 0.05;
|
||||||
|
var upperArmLength = scale * 0.2;
|
||||||
|
|
||||||
|
var lowerArmThickness = scale * 0.05;
|
||||||
|
var lowerArmLength = scale * 0.2;
|
||||||
|
|
||||||
|
var legLength = scale * 0.3;
|
||||||
|
var legThickness = scale * 0.08;
|
||||||
|
|
||||||
|
var shinLength = scale * 0.2;
|
||||||
|
var shinThickness = scale * 0.06;
|
||||||
|
|
||||||
|
var footLength = scale * 0.2;
|
||||||
|
var footThickness = scale * 0.03;
|
||||||
|
var footWidth = scale * 0.08;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// body
|
||||||
|
//
|
||||||
|
|
||||||
|
var bodyID = Entities.addEntity({
|
||||||
|
name: "ragdoll body",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: bodyDepth, y: bodyHeight, z: bodyWidth },
|
||||||
|
position: Vec3.sum(pos, { x: 0, y: scale * 0.0, z:0 }),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// head
|
||||||
|
//
|
||||||
|
|
||||||
|
var headID = Entities.addEntity({
|
||||||
|
name: "ragdoll head",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: headSize, y: headSize, z: headSize },
|
||||||
|
position: Vec3.sum(pos, { x: 0, y: bodyHeight / 2 + headSize / 2 + neckLength, z:0 }),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0.5, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("spring", headID, {
|
||||||
|
targetRotation: { x: 0, y: 0, z: 0, w: 1 },
|
||||||
|
angularTimeScale: 2.0,
|
||||||
|
otherID: bodyID,
|
||||||
|
tag: "cone-twist test spring"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var noseID = Entities.addEntity({
|
||||||
|
name: "ragdoll nose",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 100 },
|
||||||
|
dimensions: { x: headSize / 5, y: headSize / 5, z: headSize / 5 },
|
||||||
|
localPosition: { x: headSize / 2 + headSize / 10, y: 0, z: 0 },
|
||||||
|
dynamic: false,
|
||||||
|
collisionless: true,
|
||||||
|
lifetime: lifetime,
|
||||||
|
parentID: headID,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("cone-twist", headID, {
|
||||||
|
pivot: { x: 0, y: -headSize / 2 - neckLength / 2, z: 0 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
otherEntityID: bodyID,
|
||||||
|
otherPivot: { x: 0, y: bodyHeight / 2 + neckLength / 2, z: 0 },
|
||||||
|
otherAxis: { x: 0, y: 1, z: 0 },
|
||||||
|
swingSpan1: Math.PI / 4,
|
||||||
|
swingSpan2: Math.PI / 4,
|
||||||
|
twistSpan: Math.PI / 2,
|
||||||
|
tag: "ragdoll neck joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// right upper arm
|
||||||
|
//
|
||||||
|
|
||||||
|
var rightUpperArmID = Entities.addEntity({
|
||||||
|
name: "ragdoll right arm",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: upperArmThickness, y: upperArmThickness, z: upperArmLength },
|
||||||
|
position: Vec3.sum(pos, { x: 0,
|
||||||
|
y: bodyHeight / 2 + upperArmThickness / 2,
|
||||||
|
z: bodyWidth / 2 + shoulderGap + upperArmLength / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("cone-twist", bodyID, {
|
||||||
|
pivot: { x: 0, y: bodyHeight / 2 + upperArmThickness / 2, z: bodyWidth / 2 + shoulderGap / 2 },
|
||||||
|
axis: { x: 0, y: 0, z: 1 },
|
||||||
|
otherEntityID: rightUpperArmID,
|
||||||
|
otherPivot: { x: 0, y: 0, z: -upperArmLength / 2 - shoulderGap / 2 },
|
||||||
|
otherAxis: { x: 0, y: 0, z: 1 },
|
||||||
|
swingSpan1: Math.PI / 2,
|
||||||
|
swingSpan2: Math.PI / 2,
|
||||||
|
twistSpan: 0,
|
||||||
|
tag: "ragdoll right shoulder joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// left upper arm
|
||||||
|
//
|
||||||
|
|
||||||
|
var leftUpperArmID = Entities.addEntity({
|
||||||
|
name: "ragdoll left arm",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: upperArmThickness, y: upperArmThickness, z: upperArmLength },
|
||||||
|
position: Vec3.sum(pos, { x: 0,
|
||||||
|
y: bodyHeight / 2 + upperArmThickness / 2,
|
||||||
|
z: -bodyWidth / 2 - shoulderGap - upperArmLength / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("cone-twist", bodyID, {
|
||||||
|
pivot: { x: 0, y: bodyHeight / 2 + upperArmThickness / 2, z: -bodyWidth / 2 - shoulderGap / 2 },
|
||||||
|
axis: { x: 0, y: 0, z: -1 },
|
||||||
|
otherEntityID: leftUpperArmID,
|
||||||
|
otherPivot: { x: 0, y: 0, z: upperArmLength / 2 + shoulderGap / 2 },
|
||||||
|
otherAxis: { x: 0, y: 0, z: -1 },
|
||||||
|
swingSpan1: Math.PI / 2,
|
||||||
|
swingSpan2: Math.PI / 2,
|
||||||
|
twistSpan: 0,
|
||||||
|
tag: "ragdoll left shoulder joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// right lower arm
|
||||||
|
//
|
||||||
|
|
||||||
|
var rightLowerArmID = Entities.addEntity({
|
||||||
|
name: "ragdoll right lower arm",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: lowerArmThickness, y: lowerArmThickness, z: lowerArmLength },
|
||||||
|
position: Vec3.sum(pos, { x: 0,
|
||||||
|
y: bodyHeight / 2 - upperArmThickness / 2,
|
||||||
|
z: bodyWidth / 2 + shoulderGap + upperArmLength + elbowGap + lowerArmLength / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: -1, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("hinge", rightLowerArmID, {
|
||||||
|
pivot: { x: 0, y: 0, z: -lowerArmLength / 2 - elbowGap / 2 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
otherEntityID: rightUpperArmID,
|
||||||
|
otherPivot: { x: 0, y: 0, z: upperArmLength / 2 + elbowGap / 2 },
|
||||||
|
otherAxis: { x: 0, y: 1, z: 0 },
|
||||||
|
low: Math.PI / -2,
|
||||||
|
high: 0,
|
||||||
|
tag: "ragdoll right elbow joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// left lower arm
|
||||||
|
//
|
||||||
|
|
||||||
|
var leftLowerArmID = Entities.addEntity({
|
||||||
|
name: "ragdoll left lower arm",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: lowerArmThickness, y: lowerArmThickness, z: lowerArmLength },
|
||||||
|
position: Vec3.sum(pos, { x: 0,
|
||||||
|
y: bodyHeight / 2 - upperArmThickness / 2,
|
||||||
|
z: -bodyWidth / 2 - shoulderGap - upperArmLength - elbowGap - lowerArmLength / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: -1, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("hinge", leftLowerArmID, {
|
||||||
|
pivot: { x: 0, y: 0, z: lowerArmLength / 2 + elbowGap / 2 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
otherEntityID: leftUpperArmID,
|
||||||
|
otherPivot: { x: 0, y: 0, z: -upperArmLength / 2 - elbowGap / 2 },
|
||||||
|
otherAxis: { x: 0, y: 1, z: 0 },
|
||||||
|
low: 0,
|
||||||
|
high: Math.PI / 2,
|
||||||
|
tag: "ragdoll left elbow joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// right leg
|
||||||
|
//
|
||||||
|
|
||||||
|
var rightLegID = Entities.addEntity({
|
||||||
|
name: "ragdoll right arm",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 20, green: 200, red: 20 },
|
||||||
|
dimensions: { x: legThickness, y: legLength, z: legThickness },
|
||||||
|
position: Vec3.sum(pos, { x: 0, y: -bodyHeight / 2 - hipGap - legLength / 2, z: bodyWidth / 2 - legThickness / 2 }),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("cone-twist", rightLegID, {
|
||||||
|
pivot: { x: 0, y: legLength / 2 + hipGap / 2, z: 0 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
otherEntityID: bodyID,
|
||||||
|
otherPivot: { x: 0, y: -bodyHeight / 2 - hipGap / 2, z: bodyWidth / 2 - legThickness / 2 },
|
||||||
|
otherAxis: Vec3.normalize({ x: -1, y: 1, z: 0 }),
|
||||||
|
swingSpan1: Math.PI / 4,
|
||||||
|
swingSpan2: Math.PI / 4,
|
||||||
|
twistSpan: 0,
|
||||||
|
tag: "ragdoll right hip joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// left leg
|
||||||
|
//
|
||||||
|
|
||||||
|
var leftLegID = Entities.addEntity({
|
||||||
|
name: "ragdoll left arm",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 20, green: 200, red: 20 },
|
||||||
|
dimensions: { x: legThickness, y: legLength, z: legThickness },
|
||||||
|
position: Vec3.sum(pos, { x: 0, y: -bodyHeight / 2 - hipGap - legLength / 2, z: -bodyWidth / 2 + legThickness / 2 }),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("cone-twist", leftLegID, {
|
||||||
|
pivot: { x: 0, y: legLength / 2 + hipGap / 2, z: 0 },
|
||||||
|
axis: { x: 0, y: 1, z: 0 },
|
||||||
|
otherEntityID: bodyID,
|
||||||
|
otherPivot: { x: 0, y: -bodyHeight / 2 - hipGap / 2, z: -bodyWidth / 2 + legThickness / 2 },
|
||||||
|
otherAxis: Vec3.normalize({ x: -1, y: 1, z: 0 }),
|
||||||
|
swingSpan1: Math.PI / 4,
|
||||||
|
swingSpan2: Math.PI / 4,
|
||||||
|
twistSpan: 0,
|
||||||
|
tag: "ragdoll left hip joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// right shin
|
||||||
|
//
|
||||||
|
|
||||||
|
var rightShinID = Entities.addEntity({
|
||||||
|
name: "ragdoll right shin",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 20, green: 200, red: 20 },
|
||||||
|
dimensions: { x: shinThickness, y: shinLength, z: shinThickness },
|
||||||
|
position: Vec3.sum(pos, { x: 0,
|
||||||
|
y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength / 2,
|
||||||
|
z: bodyWidth / 2 - legThickness / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: -2, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("hinge", rightShinID, {
|
||||||
|
pivot: { x: 0, y: shinLength / 2 + kneeGap / 2, z: 0 },
|
||||||
|
axis: { x: 0, y: 0, z: 1 },
|
||||||
|
otherEntityID: rightLegID,
|
||||||
|
otherPivot: { x: 0, y: -legLength / 2 - kneeGap / 2, z: 0 },
|
||||||
|
otherAxis: { x: 0, y: 0, z: 1 },
|
||||||
|
low: 0,
|
||||||
|
high: Math.PI / 2,
|
||||||
|
tag: "ragdoll right knee joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// left shin
|
||||||
|
//
|
||||||
|
|
||||||
|
var leftShinID = Entities.addEntity({
|
||||||
|
name: "ragdoll left shin",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 20, green: 200, red: 20 },
|
||||||
|
dimensions: { x: shinThickness, y: shinLength, z: shinThickness },
|
||||||
|
position: Vec3.sum(pos, { x: 0,
|
||||||
|
y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength / 2,
|
||||||
|
z: -bodyWidth / 2 + legThickness / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: -2, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("hinge", leftShinID, {
|
||||||
|
pivot: { x: 0, y: shinLength / 2 + kneeGap / 2, z: 0 },
|
||||||
|
axis: { x: 0, y: 0, z: 1 },
|
||||||
|
otherEntityID: leftLegID,
|
||||||
|
otherPivot: { x: 0, y: -legLength / 2 - kneeGap / 2, z: 0 },
|
||||||
|
otherAxis: { x: 0, y: 0, z: 1 },
|
||||||
|
low: 0,
|
||||||
|
high: Math.PI / 2,
|
||||||
|
tag: "ragdoll left knee joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// right foot
|
||||||
|
//
|
||||||
|
|
||||||
|
var rightFootID = Entities.addEntity({
|
||||||
|
name: "ragdoll right foot",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: footLength, y: footThickness, z: footWidth },
|
||||||
|
position: Vec3.sum(pos, { x: -shinThickness / 2 + footLength / 2,
|
||||||
|
y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength - ankleGap - footThickness / 2,
|
||||||
|
z: bodyWidth / 2 - legThickness / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: -5, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("hinge", rightFootID, {
|
||||||
|
pivot: { x: -footLength / 2 + shinThickness / 2, y: ankleGap / 2, z: 0 },
|
||||||
|
axis: { x: 0, y: 0, z: 1 },
|
||||||
|
otherEntityID: rightShinID,
|
||||||
|
otherPivot: { x: 0, y: -shinLength / 2 - ankleGap / 2, z: 0 },
|
||||||
|
otherAxis: { x: 0, y: 0, z: 1 },
|
||||||
|
low: ankleMin,
|
||||||
|
high: ankleMax,
|
||||||
|
tag: "ragdoll right ankle joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// left foot
|
||||||
|
//
|
||||||
|
|
||||||
|
var leftFootID = Entities.addEntity({
|
||||||
|
name: "ragdoll left foot",
|
||||||
|
type: "Box",
|
||||||
|
color: { blue: 128, green: 100, red: 20 },
|
||||||
|
dimensions: { x: footLength, y: footThickness, z: footWidth },
|
||||||
|
position: Vec3.sum(pos, { x: -shinThickness / 2 + footLength / 2,
|
||||||
|
y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength - ankleGap - footThickness / 2,
|
||||||
|
z: bodyWidth / 2 - legThickness / 2
|
||||||
|
}),
|
||||||
|
dynamic: true,
|
||||||
|
collisionless: false,
|
||||||
|
gravity: { x: 0, y: -5, z: 0 },
|
||||||
|
lifetime: lifetime,
|
||||||
|
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
|
||||||
|
});
|
||||||
|
|
||||||
|
Entities.addAction("hinge", leftFootID, {
|
||||||
|
pivot: { x: -footLength / 2 + shinThickness / 2, y: ankleGap / 2, z: 0 },
|
||||||
|
axis: { x: 0, y: 0, z: 1 },
|
||||||
|
otherEntityID: leftShinID,
|
||||||
|
otherPivot: { x: 0, y: -shinLength / 2 - ankleGap / 2, z: 0 },
|
||||||
|
otherAxis: { x: 0, y: 0, z: 1 },
|
||||||
|
low: ankleMin,
|
||||||
|
high: ankleMax,
|
||||||
|
tag: "ragdoll left ankle joint"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWebEventReceived(eventString) {
|
||||||
|
print("received web event: " + JSON.stringify(eventString));
|
||||||
|
if (typeof eventString === "string") {
|
||||||
|
var event;
|
||||||
|
try {
|
||||||
|
event = JSON.parse(eventString);
|
||||||
|
} catch(e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event["dynamics-tests-command"]) {
|
||||||
|
var commandToFunctionMap = {
|
||||||
|
"cone-twist-and-spring-lever-test": coneTwistAndSpringLeverTest,
|
||||||
|
"door-vs-world-test": doorVSWorldTest,
|
||||||
|
"hinge-chain-test": hingeChainTest,
|
||||||
|
"slider-vs-world-test": sliderVSWorldTest,
|
||||||
|
"slider-chain-test": sliderChainTest,
|
||||||
|
"ball-socket-between-test": ballSocketBetweenTest,
|
||||||
|
"ball-socket-coincident-test": ballSocketCoincidentTest,
|
||||||
|
"ragdoll-test": ragdollTest
|
||||||
|
};
|
||||||
|
|
||||||
|
var cmd = event["dynamics-tests-command"];
|
||||||
|
if (commandToFunctionMap.hasOwnProperty(cmd)) {
|
||||||
|
var func = commandToFunctionMap[cmd];
|
||||||
|
func(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var onDynamicsTestsScreen = false;
|
||||||
|
var shouldActivateButton = false;
|
||||||
|
|
||||||
|
function onClicked() {
|
||||||
|
if (onDynamicsTestsScreen) {
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
} else {
|
||||||
|
shouldActivateButton = true;
|
||||||
|
tablet.gotoWebScreen(DYNAMICS_TESTS_URL +
|
||||||
|
"?lifetime=" + DEFAULT_LIFETIME.toString()
|
||||||
|
);
|
||||||
|
onDynamicsTestsScreen = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScreenChanged() {
|
||||||
|
// for toolbar mode: change button to active when window is first openend, false otherwise.
|
||||||
|
button.editProperties({isActive: shouldActivateButton});
|
||||||
|
shouldActivateButton = false;
|
||||||
|
onDynamicsTestsScreen = shouldActivateButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
button.clicked.disconnect(onClicked);
|
||||||
|
tablet.removeButton(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.clicked.connect(onClicked);
|
||||||
|
tablet.webEventReceived.connect(onWebEventReceived);
|
||||||
|
tablet.screenChanged.connect(onScreenChanged);
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
|
}()); // END LOCAL_SCOPE
|
69
scripts/developer/tests/dynamics/dynamicsTests.svg
Normal file
69
scripts/developer/tests/dynamics/dynamicsTests.svg
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
viewBox="0 0 744.09448819 1052.3622047"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="dynamicsTests.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.35"
|
||||||
|
inkscape:cx="-482.14286"
|
||||||
|
inkscape:cy="520"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="2815"
|
||||||
|
inkscape:window-height="1776"
|
||||||
|
inkscape:window-x="65"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
sodipodi:type="spiral"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path4136"
|
||||||
|
sodipodi:cx="351.42856"
|
||||||
|
sodipodi:cy="532.36218"
|
||||||
|
sodipodi:expansion="1"
|
||||||
|
sodipodi:revolution="3"
|
||||||
|
sodipodi:radius="189.2628"
|
||||||
|
sodipodi:argument="-18.34539"
|
||||||
|
sodipodi:t0="0"
|
||||||
|
d="m 351.42856,532.36218 c 8.30865,4.58408 -2.08005,13.46152 -7.61904,13.80953 -15.01033,0.94307 -22.49738,-16.46931 -20.00001,-29.04761 4.46719,-22.49963 30.09017,-32.38647 50.47618,-26.1905 29.91728,9.09285 42.49805,43.8703 32.38097,71.90475 -13.48446,37.36552 -57.7036,52.70019 -93.33331,38.57147 -44.83636,-17.77957 -62.94832,-71.56146 -44.76195,-114.76189 22.02613,-52.32152 85.43296,-73.22291 136.19046,-50.95243 59.81595,26.24498 83.51408,99.31294 57.14291,157.61902 -30.44656,67.31668 -113.1986,93.81634 -179.0476,63.3334 C 208.03536,622.01126 178.73083,529.55968 213.33329,456.17176 252.15203,373.8416 354.3141,341.72978 435.23803,380.45739 c 89.8409,42.99499 124.76183,154.87556 81.90485,243.3333"
|
||||||
|
transform="matrix(1.6331701,0,0,2.2153757,-219.59811,-629.37371)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
Loading…
Reference in a new issue