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

This commit is contained in:
Sam Gateau 2017-05-18 09:37:04 -07:00
commit d9ad45bf4a
51 changed files with 455 additions and 753 deletions

View file

@ -53,31 +53,3 @@ QVariantMap AssignmentDynamic::getArguments() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getArguments called in assignment-client.";
return QVariantMap();
}
glm::vec3 AssignmentDynamic::getPosition() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getPosition called in assignment-client.";
return glm::vec3(0.0f);
}
glm::quat AssignmentDynamic::getRotation() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getRotation called in assignment-client.";
return glm::quat();
}
glm::vec3 AssignmentDynamic::getLinearVelocity() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getLinearVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::setLinearVelocity called in assignment-client.";
}
glm::vec3 AssignmentDynamic::getAngularVelocity() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getAngularVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::setAngularVelocity called in assignment-client.";
}

View file

@ -39,13 +39,6 @@ private:
QByteArray _data;
protected:
virtual glm::vec3 getPosition() override;
virtual glm::quat getRotation() override;
virtual glm::vec3 getLinearVelocity() override;
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
virtual glm::vec3 getAngularVelocity() override;
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
bool _active;
EntityItemWeakPointer _ownerEntity;
};

View file

@ -145,7 +145,7 @@ private:
std::unordered_map<QUuid, QVector<JointData>> _lastOtherAvatarSentJoints;
uint64_t _identityChangeTimestamp;
bool _avatarSessionDisplayNameMustChange{ false };
bool _avatarSessionDisplayNameMustChange{ true };
int _numAvatarsSentLastFrame = 0;
int _numFramesSinceAdjustment = 0;

View file

@ -56,43 +56,64 @@
"jointName": "Hips",
"positionVar": "hipsPosition",
"rotationVar": "hipsRotation",
"typeVar": "hipsType"
"typeVar": "hipsType",
"weightVar": "hipsWeight",
"weight": 1.0,
"flexCoefficients": [1]
},
{
"jointName": "RightHand",
"positionVar": "rightHandPosition",
"rotationVar": "rightHandRotation",
"typeVar": "rightHandType"
"typeVar": "rightHandType",
"weightVar": "rightHandWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.25, 0.1, 0.05, 0.01, 0.0, 0.0]
},
{
"jointName": "LeftHand",
"positionVar": "leftHandPosition",
"rotationVar": "leftHandRotation",
"typeVar": "leftHandType"
"typeVar": "leftHandType",
"weightVar": "leftHandWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.5, 0.5, 0.25, 0.1, 0.05, 0.01, 0.0, 0.0]
},
{
"jointName": "RightFoot",
"positionVar": "rightFootPosition",
"rotationVar": "rightFootRotation",
"typeVar": "rightFootType"
"typeVar": "rightFootType",
"weightVar": "rightFootWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.45, 0.45]
},
{
"jointName": "LeftFoot",
"positionVar": "leftFootPosition",
"rotationVar": "leftFootRotation",
"typeVar": "leftFootType"
"typeVar": "leftFootType",
"weightVar": "leftFootWeight",
"weight": 1.0,
"flexCoefficients": [1, 0.45, 0.45]
},
{
"jointName": "Spine2",
"positionVar": "spine2Position",
"rotationVar": "spine2Rotation",
"typeVar": "spine2Type"
"typeVar": "spine2Type",
"weightVar": "spine2Weight",
"weight": 1.0,
"flexCoefficients": [0.45, 0.45]
},
{
"jointName": "Head",
"positionVar": "headPosition",
"rotationVar": "headRotation",
"typeVar": "headType"
"typeVar": "headType",
"weightVar": "headWeight",
"weight": 4.0,
"flexCoefficients": [1, 0.5, 0.5, 0.5, 0.5]
}
]
},

View file

@ -132,62 +132,16 @@ Item {
color: hifi.colors.textFieldLightBackground
border.color: hifi.colors.blueHighlight
border.width: 0
TextInput {
id: myDisplayNameText
// Properties
text: thisNameCard.displayName
maximumLength: 256
clip: true
// Size
width: parent.width
height: parent.height
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
anchors.right: parent.right
anchors.rightMargin: editGlyph.width + editGlyph.anchors.rightMargin
// Style
color: hifi.colors.darkGray
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
font.family: firaSansSemiBold.name
font.pixelSize: displayNameTextPixelSize
selectionColor: hifi.colors.blueAccent
selectedTextColor: "black"
// Text Positioning
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
autoScroll: false;
// Signals
onEditingFinished: {
if (MyAvatar.displayName !== text) {
MyAvatar.displayName = text;
UserActivityLogger.palAction("display_name_change", text);
}
cursorPosition = 0
focus = false
myDisplayName.border.width = 0
color = hifi.colors.darkGray
pal.currentlyEditingDisplayName = false
autoScroll = false;
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
myDisplayName.border.width = 1
myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll();
myDisplayNameText.focus = true
myDisplayNameText.color = "black"
pal.currentlyEditingDisplayName = true
myDisplayNameText.autoScroll = true;
myDisplayNameText.focus = true;
myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX - myDisplayNameText.anchors.leftMargin, mouseY, TextInput.CursorOnCharacter);
}
onDoubleClicked: {
myDisplayNameText.selectAll();
myDisplayNameText.focus = true;
pal.currentlyEditingDisplayName = true
myDisplayNameText.autoScroll = true;
}
onEntered: myDisplayName.color = hifi.colors.lightGrayText;
onExited: myDisplayName.color = hifi.colors.textFieldLightBackground;
@ -207,6 +161,54 @@ Item {
verticalAlignment: Text.AlignVCenter
color: hifi.colors.baseGray
}
TextInput {
id: myDisplayNameText
// Properties
text: thisNameCard.displayName
maximumLength: 256
clip: true
// Size
width: parent.width
height: parent.height
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
anchors.right: parent.right
anchors.rightMargin: editGlyph.width + editGlyph.anchors.rightMargin
// Style
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
font.family: firaSansSemiBold.name
font.pixelSize: displayNameTextPixelSize
selectionColor: hifi.colors.blueAccent
selectedTextColor: "black"
// Text Positioning
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
autoScroll: false;
// Signals
onEditingFinished: {
if (MyAvatar.displayName !== text) {
MyAvatar.displayName = text;
UserActivityLogger.palAction("display_name_change", text);
}
focus = false;
}
onFocusChanged: {
if (focus === true) {
myDisplayName.border.width = 1
color = "black"
autoScroll = true;
pal.currentlyEditingDisplayName = true
} else {
myDisplayName.border.width = 0
color: hifi.colors.darkGray
cursorPosition = 0;
autoScroll = false;
pal.currentlyEditingDisplayName = false
}
}
}
}
// DisplayName container for others' cards
Item {

View file

@ -5258,9 +5258,8 @@ void Application::nodeActivated(SharedNodePointer node) {
}
if (node->getType() == NodeType::AvatarMixer) {
// new avatar mixer, send off our identity packet right away
// new avatar mixer, send off our identity packet on next update loop
getMyAvatar()->markIdentityDataChanged();
getMyAvatar()->sendIdentityPacket();
getMyAvatar()->resetLastSent();
}
}

View file

@ -14,7 +14,6 @@
#include <avatar/AvatarActionHold.h>
#include <avatar/AvatarActionFarGrab.h>
#include <ObjectActionOffset.h>
#include <ObjectActionSpring.h>
#include <ObjectActionTractor.h>
#include <ObjectActionTravelOriented.h>
#include <ObjectConstraintHinge.h>
@ -33,7 +32,7 @@ EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid
case DYNAMIC_TYPE_OFFSET:
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
case DYNAMIC_TYPE_SPRING:
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
qDebug() << "The 'spring' Action is deprecated. Replacing with 'tractor' Action.";
case DYNAMIC_TYPE_TRACTOR:
return std::make_shared<ObjectActionTractor>(id, ownerEntity);
case DYNAMIC_TYPE_HOLD:

View file

@ -189,6 +189,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
if (shape) {
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
motionState->setMass(avatar->computeMass());
avatar->setPhysicsCallback([=] (uint32_t flags) { motionState->addDirtyFlags(flags); });
_motionStates.insert(avatar.get(), motionState);
_motionStatesToAddToPhysics.insert(motionState);

View file

@ -19,9 +19,6 @@
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
assert(_avatar);
_type = MOTIONSTATE_TYPE_AVATAR;
if (_shape) {
_mass = 100.0f; // HACK
}
}
AvatarMotionState::~AvatarMotionState() {

View file

@ -239,6 +239,7 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
});
connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete()));
_characterController.setDensity(_density);
}
MyAvatar::~MyAvatar() {

View file

@ -125,7 +125,7 @@ class MyAvatar : public Avatar {
Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose)
Q_PROPERTY(float energy READ getEnergy WRITE setEnergy)
Q_PROPERTY(float isAway READ getIsAway WRITE setAway)
Q_PROPERTY(bool isAway READ getIsAway WRITE setAway)
Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled)
Q_PROPERTY(bool collisionsEnabled READ getCollisionsEnabled WRITE setCollisionsEnabled)

View file

@ -49,12 +49,9 @@ void MyCharacterController::updateShapeIfNecessary() {
// create RigidBody if it doesn't exist
if (!_rigidBody) {
btCollisionShape* shape = computeShape();
// HACK: use some simple mass property defaults for now
const btScalar DEFAULT_AVATAR_MASS = 100.0f;
const btVector3 DEFAULT_AVATAR_INERTIA_TENSOR(30.0f, 8.0f, 30.0f);
_rigidBody = new btRigidBody(DEFAULT_AVATAR_MASS, nullptr, shape, DEFAULT_AVATAR_INERTIA_TENSOR);
btScalar mass = 1.0f;
btVector3 inertia(1.0f, 1.0f, 1.0f);
_rigidBody = new btRigidBody(mass, nullptr, shape, inertia);
} else {
btCollisionShape* shape = _rigidBody->getCollisionShape();
if (shape) {
@ -63,6 +60,7 @@ void MyCharacterController::updateShapeIfNecessary() {
shape = computeShape();
_rigidBody->setCollisionShape(shape);
}
updateMassProperties();
_rigidBody->setSleepingThresholds(0.0f, 0.0f);
_rigidBody->setAngularFactor(0.0f);
@ -331,3 +329,23 @@ void MyCharacterController::initRayShotgun(const btCollisionWorld* world) {
}
}
}
void MyCharacterController::updateMassProperties() {
assert(_rigidBody);
// the inertia tensor of a capsule with Y-axis of symmetry, radius R and cylinder height H is:
// Ix = density * (volumeCylinder * (H^2 / 12 + R^2 / 4) + volumeSphere * (2R^2 / 5 + H^2 / 2 + 3HR / 8))
// Iy = density * (volumeCylinder * (R^2 / 2) + volumeSphere * (2R^2 / 5)
btScalar r2 = _radius * _radius;
btScalar h2 = 4.0f * _halfHeight * _halfHeight;
btScalar volumeSphere = 4.0f * PI * r2 * _radius / 3.0f;
btScalar volumeCylinder = TWO_PI * r2 * 2.0f * _halfHeight;
btScalar cylinderXZ = volumeCylinder * (h2 / 12.0f + r2 / 4.0f);
btScalar capsXZ = volumeSphere * (2.0f * r2 / 5.0f + h2 / 2.0f + 6.0f * _halfHeight * _radius / 8.0f);
btScalar inertiaXZ = _density * (cylinderXZ + capsXZ);
btScalar inertiaY = _density * ((volumeCylinder * r2 / 2.0f) + volumeSphere * (2.0f * r2 / 5.0f));
btVector3 inertia(inertiaXZ, inertiaY, inertiaXZ);
btScalar mass = _density * (volumeCylinder + volumeSphere);
_rigidBody->setMassProps(mass, inertia);
}

View file

@ -40,8 +40,11 @@ public:
/// return true if RayShotgun hits anything
bool testRayShotgun(const glm::vec3& position, const glm::vec3& step, RayShotgunResult& result);
void setDensity(btScalar density) { _density = density; }
protected:
void initRayShotgun(const btCollisionWorld* world);
void updateMassProperties() override;
private:
btConvexHullShape* computeShape() const;
@ -52,6 +55,7 @@ protected:
// shotgun scan data
btAlignedObjectArray<btVector3> _topPoints;
btAlignedObjectArray<btVector3> _bottomPoints;
btScalar _density { 1.0f };
};
#endif // hifi_MyCharacterController_h

View file

@ -21,6 +21,39 @@
#include "SwingTwistConstraint.h"
#include "AnimationLogging.h"
AnimInverseKinematics::IKTargetVar::IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn,
const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn) :
jointName(jointNameIn),
positionVar(positionVarIn),
rotationVar(rotationVarIn),
typeVar(typeVarIn),
weightVar(weightVarIn),
weight(weightIn),
numFlexCoefficients(flexCoefficientsIn.size()),
jointIndex(-1)
{
numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS);
for (size_t i = 0; i < numFlexCoefficients; i++) {
flexCoefficients[i] = flexCoefficientsIn[i];
}
}
AnimInverseKinematics::IKTargetVar::IKTargetVar(const IKTargetVar& orig) :
jointName(orig.jointName),
positionVar(orig.positionVar),
rotationVar(orig.rotationVar),
typeVar(orig.typeVar),
weightVar(orig.weightVar),
weight(orig.weight),
numFlexCoefficients(orig.numFlexCoefficients),
jointIndex(orig.jointIndex)
{
numFlexCoefficients = std::min(numFlexCoefficients, (size_t)MAX_FLEX_COEFFICIENTS);
for (size_t i = 0; i < numFlexCoefficients; i++) {
flexCoefficients[i] = orig.flexCoefficients[i];
}
}
AnimInverseKinematics::AnimInverseKinematics(const QString& id) : AnimNode(AnimNode::Type::InverseKinematics, id) {
}
@ -60,26 +93,22 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con
}
}
void AnimInverseKinematics::setTargetVars(
const QString& jointName,
const QString& positionVar,
const QString& rotationVar,
const QString& typeVar) {
void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar,
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients) {
IKTargetVar targetVar(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients);
// if there are dups, last one wins.
bool found = false;
for (auto& targetVar: _targetVarVec) {
if (targetVar.jointName == jointName) {
// update existing targetVar
targetVar.positionVar = positionVar;
targetVar.rotationVar = rotationVar;
targetVar.typeVar = typeVar;
for (auto& targetVarIter: _targetVarVec) {
if (targetVarIter.jointName == jointName) {
targetVarIter = targetVar;
found = true;
break;
}
}
if (!found) {
// create a new entry
_targetVarVec.push_back(IKTargetVar(jointName, positionVar, rotationVar, typeVar));
_targetVarVec.push_back(targetVar);
}
}
@ -107,10 +136,15 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
glm::quat rotation = animVars.lookupRigToGeometry(targetVar.rotationVar, defaultPose.rot());
glm::vec3 translation = animVars.lookupRigToGeometry(targetVar.positionVar, defaultPose.trans());
float weight = animVars.lookup(targetVar.weightVar, targetVar.weight);
target.setPose(rotation, translation);
target.setIndex(targetVar.jointIndex);
target.setWeight(weight);
target.setFlexCoefficients(targetVar.numFlexCoefficients, targetVar.flexCoefficients);
targets.push_back(target);
if (targetVar.jointIndex > _maxTargetIndex) {
_maxTargetIndex = targetVar.jointIndex;
}
@ -271,6 +305,8 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
// cache tip absolute position
glm::vec3 tipPosition = absolutePoses[tipIndex].trans();
size_t chainDepth = 1;
// descend toward root, pivoting each joint to get tip closer to target position
while (pivotIndex != _hipsIndex && pivotsParentIndex != -1) {
// compute the two lines that should be aligned
@ -312,9 +348,8 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
float angle = acosf(cosAngle);
const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f;
if (angle > MIN_ADJUSTMENT_ANGLE) {
// reduce angle by a fraction (for stability)
const float STABILITY_FRACTION = 0.5f;
angle *= STABILITY_FRACTION;
// reduce angle by a flexCoefficient
angle *= target.getFlexCoefficient(chainDepth);
deltaRotation = glm::angleAxis(angle, axis);
// The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's
@ -385,6 +420,8 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
pivotIndex = pivotsParentIndex;
pivotsParentIndex = _skeleton->getParentIndex(pivotIndex);
chainDepth++;
}
return lowestMovedIndex;
}
@ -806,7 +843,7 @@ void AnimInverseKinematics::initConstraints() {
stConstraint->setTwistLimits(-MAX_SHOULDER_TWIST, MAX_SHOULDER_TWIST);
std::vector<float> minDots;
const float MAX_SHOULDER_SWING = PI / 20.0f;
const float MAX_SHOULDER_SWING = PI / 16.0f;
minDots.push_back(cosf(MAX_SHOULDER_SWING));
stConstraint->setSwingLimits(minDots);

View file

@ -32,7 +32,8 @@ public:
void loadPoses(const AnimPoseVec& poses);
void computeAbsolutePoses(AnimPoseVec& absolutePoses) const;
void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar, const QString& typeVar);
void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar,
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients);
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override;
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
@ -77,22 +78,20 @@ protected:
AnimInverseKinematics(const AnimInverseKinematics&) = delete;
AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete;
enum FlexCoefficients { MAX_FLEX_COEFFICIENTS = 10 };
struct IKTargetVar {
IKTargetVar(const QString& jointNameIn,
const QString& positionVarIn,
const QString& rotationVarIn,
const QString& typeVarIn) :
positionVar(positionVarIn),
rotationVar(rotationVarIn),
typeVar(typeVarIn),
jointName(jointNameIn),
jointIndex(-1)
{}
IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn,
const QString& typeVarIn, const QString& weightVarIn, float weightIn, const std::vector<float>& flexCoefficientsIn);
IKTargetVar(const IKTargetVar& orig);
QString jointName;
QString positionVar;
QString rotationVar;
QString typeVar;
QString jointName;
QString weightVar;
float weight;
float flexCoefficients[MAX_FLEX_COEFFICIENTS];
size_t numFlexCoefficients;
int jointIndex; // cached joint index
};

View file

@ -173,6 +173,13 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
} \
float NAME = (float)NAME##_VAL.toDouble()
#define READ_OPTIONAL_FLOAT(NAME, JSON_OBJ, DEFAULT) \
auto NAME##_VAL = JSON_OBJ.value(#NAME); \
float NAME = (float)DEFAULT; \
if (NAME##_VAL.isDouble()) { \
NAME = (float)NAME##_VAL.toDouble(); \
} \
do {} while (0)
static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) {
auto idVal = jsonObj.value("id");
@ -470,8 +477,21 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
READ_STRING(positionVar, targetObj, id, jsonUrl, nullptr);
READ_STRING(rotationVar, targetObj, id, jsonUrl, nullptr);
READ_OPTIONAL_STRING(typeVar, targetObj);
READ_OPTIONAL_STRING(weightVar, targetObj);
READ_OPTIONAL_FLOAT(weight, targetObj, 1.0f);
node->setTargetVars(jointName, positionVar, rotationVar, typeVar);
auto flexCoefficientsValue = targetObj.value("flexCoefficients");
if (!flexCoefficientsValue.isArray()) {
qCCritical(animation) << "AnimNodeLoader, bad or missing flexCoefficients array in \"targets\", id =" << id << ", url =" << jsonUrl.toDisplayString();
return nullptr;
}
auto flexCoefficientsArray = flexCoefficientsValue.toArray();
std::vector<float> flexCoefficients;
for (const auto& value : flexCoefficientsArray) {
flexCoefficients.push_back((float)value.toDouble());
}
node->setTargetVars(jointName, positionVar, rotationVar, typeVar, weightVar, weight, flexCoefficients);
};
READ_OPTIONAL_STRING(solutionSource, jsonObj);

View file

@ -14,6 +14,23 @@ void IKTarget::setPose(const glm::quat& rotation, const glm::vec3& translation)
_pose.trans() = translation;
}
void IKTarget::setFlexCoefficients(size_t numFlexCoefficientsIn, const float* flexCoefficientsIn) {
_numFlexCoefficients = std::min(numFlexCoefficientsIn, (size_t)MAX_FLEX_COEFFICIENTS);
for (size_t i = 0; i < _numFlexCoefficients; i++) {
_flexCoefficients[i] = flexCoefficientsIn[i];
}
}
float IKTarget::getFlexCoefficient(size_t chainDepth) const {
const float DEFAULT_FLEX_COEFFICIENT = 0.5f;
if (chainDepth < _numFlexCoefficients) {
return _flexCoefficients[chainDepth];
} else {
return DEFAULT_FLEX_COEFFICIENT;
}
}
void IKTarget::setType(int type) {
switch (type) {
case (int)Type::RotationAndPosition:

View file

@ -35,15 +35,21 @@ public:
void setPose(const glm::quat& rotation, const glm::vec3& translation);
void setIndex(int index) { _index = index; }
void setType(int);
void setFlexCoefficients(size_t numFlexCoefficientsIn, const float* flexCoefficientsIn);
float getFlexCoefficient(size_t chainDepth) const;
// HACK: give HmdHead targets more "weight" during IK algorithm
float getWeight() const { return _type == Type::HmdHead ? HACK_HMD_TARGET_WEIGHT : 1.0f; }
void setWeight(float weight) { _weight = weight; }
float getWeight() const { return _weight; }
enum FlexCoefficients { MAX_FLEX_COEFFICIENTS = 10 };
private:
AnimPose _pose;
int _index{-1};
Type _type{Type::RotationAndPosition};
float _weight;
float _flexCoefficients[MAX_FLEX_COEFFICIENTS];
size_t _numFlexCoefficients;
};
#endif // hifi_IKTarget_h

View file

@ -1088,10 +1088,12 @@ void Rig::updateHeadAnimVars(const HeadParameters& params) {
// Since there is an explicit hips ik target, switch the head to use the more generic RotationAndPosition IK chain type.
// this will allow the spine to bend more, ensuring that it can reach the head target position.
_animVars.set("headType", (int)IKTarget::Type::RotationAndPosition);
_animVars.unset("headWeight"); // use the default weight for this target.
} else {
// When there is no hips IK target, use the HmdHead IK chain type. This will make the spine very stiff,
// but because the IK _hipsOffset is enabled, the hips will naturally follow underneath the head.
_animVars.set("headType", (int)IKTarget::Type::HmdHead);
_animVars.set("headWeight", 8.0f);
}
} else {
_animVars.unset("headPosition");
@ -1400,22 +1402,24 @@ void Rig::computeAvatarBoundingCapsule(
AnimInverseKinematics ikNode("boundingShape");
ikNode.setSkeleton(_animSkeleton);
// AJT: FIX ME!!!!! ensure that empty weights vector does something reasonable....
ikNode.setTargetVars("LeftHand",
"leftHandPosition",
"leftHandRotation",
"leftHandType");
"leftHandType", "leftHandWeight", 1.0f, {});
ikNode.setTargetVars("RightHand",
"rightHandPosition",
"rightHandRotation",
"rightHandType");
"rightHandType", "rightHandWeight", 1.0f, {});
ikNode.setTargetVars("LeftFoot",
"leftFootPosition",
"leftFootRotation",
"leftFootType");
"leftFootType", "leftFootWeight", 1.0f, {});
ikNode.setTargetVars("RightFoot",
"rightFootPosition",
"rightFootRotation",
"rightFootType");
"rightFootType", "rightFootWeight", 1.0f, {});
AnimPose geometryToRig = _modelOffset * _geometryOffset;

View file

@ -1265,6 +1265,17 @@ void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
radius = halfExtents.x;
}
float Avatar::computeMass() {
float radius;
glm::vec3 start, end;
getCapsule(start, end, radius);
// NOTE:
// volumeOfCapsule = volumeOfCylinder + volumeOfSphere
// volumeOfCapsule = (2PI * R^2 * H) + (4PI * R^3 / 3)
// volumeOfCapsule = 2PI * R^2 * (H + 2R/3)
return _density * TWO_PI * radius * radius * (glm::length(end - start) + 2.0f * radius / 3.0f);
}
// virtual
void Avatar::rebuildCollisionShape() {
addPhysicsFlags(Simulation::DIRTY_SHAPE);

View file

@ -195,6 +195,7 @@ public:
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
void getCapsule(glm::vec3& start, glm::vec3& end, float& radius);
float computeMass();
using SpatiallyNestable::setPosition;
virtual void setPosition(const glm::vec3& position) override;

View file

@ -52,6 +52,7 @@ const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData";
static const int TRANSLATION_COMPRESSION_RADIX = 12;
static const int SENSOR_TO_WORLD_SCALE_RADIX = 10;
static const float AUDIO_LOUDNESS_SCALE = 1024.0f;
static const float DEFAULT_AVATAR_DENSITY = 1000.0f; // density of water
#define ASSERT(COND) do { if (!(COND)) { abort(); } } while(0)
@ -65,7 +66,8 @@ AvatarData::AvatarData() :
_headData(NULL),
_errorLogExpiry(0),
_owningAvatarMixer(),
_targetVelocity(0.0f)
_targetVelocity(0.0f),
_density(DEFAULT_AVATAR_DENSITY)
{
setBodyPitch(0.0f);
setBodyYaw(-90.0f);
@ -1588,8 +1590,6 @@ void AvatarData::setDisplayName(const QString& displayName) {
_displayName = displayName;
_sessionDisplayName = "";
sendIdentityPacket();
qCDebug(avatars) << "Changing display name for avatar to" << displayName;
markIdentityDataChanged();
}

View file

@ -629,6 +629,8 @@ public:
_identityUpdatedAt = usecTimestampNow();
}
float getDensity() const { return _density; }
signals:
void displayNameChanged();
@ -785,6 +787,7 @@ protected:
bool _identityDataChanged { false };
quint64 _identityUpdatedAt { 0 };
float _density;
private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);

View file

@ -43,14 +43,14 @@
| |
+--------------------+ +-----------------------+
| | | |
| ObjectActionSpring | | ObjectConstraintHinge |
| ObjectActionTractor| | ObjectConstraintHinge |
| (physics) | | (physics) |
+--------------------+ +-----------------------+
A dynamic is a callback which is registered with bullet. A dynamic is called-back every physics
simulation step and can do whatever it wants with the various datastructures it has available. An
simulation step and can do whatever it wants with the various datastructures it has available. A
dynamic, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that
point by a spring.
@ -60,7 +60,7 @@ script or when receiving information via an EntityTree data-stream (either over
svo file).
In the interface, if an EntityItem has dynamics, this EntityItem will have pointers to ObjectDynamic
subclass (like ObjectDynamicSpring) instantiations. Code in the entities library affects a dynamic-object
subclass (like ObjectDynamicTractor) instantiations. Code in the entities library affects a dynamic-object
via the EntityDynamicInterface (which knows nothing about bullet). When the ObjectDynamic subclass
instance is created, it is registered as a dynamic with bullet. Bullet will call into code in this
instance with the btDynamicInterface every physics-simulation step.
@ -75,11 +75,11 @@ right now the AssignmentDynamic class is a place-holder.
The dynamic-objects are instantiated by singleton (dependecy) subclasses of EntityDynamicFactoryInterface.
In the interface, the subclass is an InterfaceDynamicFactory and it will produce things like
ObjectDynamicSpring. In an entity-server the subclass is an AssignmentDynamicFactory and it always
ObjectDynamicTractor. In an entity-server the subclass is an AssignmentDynamicFactory and it always
produces AssignmentDynamics.
Depending on the dynamic's type, it will have various arguments. When a script changes an argument of an
dynamic, the argument-holding member-variables of ObjectDynamicSpring (in this example) are updated and
dynamic, the argument-holding member-variables of ObjectDynamicTractor (in this example) are updated and
also serialized into _dynamicData in the EntityItem. Each subclass of ObjectDynamic knows how to serialize
and deserialize its own arguments. _dynamicData is what gets sent over the wire or saved in an svo file.
When a packet-reader receives data for _dynamicData, it will save it in the EntityItem; this causes the
@ -103,7 +103,7 @@ EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicT
return DYNAMIC_TYPE_OFFSET;
}
if (normalizedDynamicTypeString == "spring") {
return DYNAMIC_TYPE_SPRING;
return DYNAMIC_TYPE_TRACTOR;
}
if (normalizedDynamicTypeString == "tractor") {
return DYNAMIC_TYPE_TRACTOR;
@ -142,7 +142,6 @@ QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicTyp
case DYNAMIC_TYPE_OFFSET:
return "offset";
case DYNAMIC_TYPE_SPRING:
return "spring";
case DYNAMIC_TYPE_TRACTOR:
return "tractor";
case DYNAMIC_TYPE_HOLD:

View file

@ -94,15 +94,6 @@ public:
QString argumentName, bool& ok, bool required = true);
protected:
virtual glm::vec3 getPosition() = 0;
// virtual void setPosition(glm::vec3 position) = 0;
virtual glm::quat getRotation() = 0;
// virtual void setRotation(glm::quat rotation) = 0;
virtual glm::vec3 getLinearVelocity() = 0;
virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
virtual glm::vec3 getAngularVelocity() = 0;
virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
QUuid _id;
EntityDynamicType _type;
bool _active { false };

View file

@ -1691,14 +1691,20 @@ void EntityItem::updateVelocity(const glm::vec3& value) {
setLocalVelocity(Vectors::ZERO);
}
} else {
const float MIN_LINEAR_SPEED = 0.001f;
if (glm::length(value) < MIN_LINEAR_SPEED) {
velocity = ENTITY_ITEM_ZERO_VEC3;
} else {
velocity = value;
float speed = glm::length(value);
if (!glm::isnan(speed)) {
const float MIN_LINEAR_SPEED = 0.001f;
const float MAX_LINEAR_SPEED = 270.0f; // 3m per step at 90Hz
if (speed < MIN_LINEAR_SPEED) {
velocity = ENTITY_ITEM_ZERO_VEC3;
} else if (speed > MAX_LINEAR_SPEED) {
velocity = (MAX_LINEAR_SPEED / speed) * value;
} else {
velocity = value;
}
setLocalVelocity(velocity);
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
}
setLocalVelocity(velocity);
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
}
}
}
@ -1723,8 +1729,16 @@ void EntityItem::updateGravity(const glm::vec3& value) {
if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
_gravity = Vectors::ZERO;
} else {
_gravity = value;
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
float magnitude = glm::length(value);
if (!glm::isnan(magnitude)) {
const float MAX_ACCELERATION_OF_GRAVITY = 10.0f * 9.8f; // 10g
if (magnitude > MAX_ACCELERATION_OF_GRAVITY) {
_gravity = (MAX_ACCELERATION_OF_GRAVITY / magnitude) * value;
} else {
_gravity = value;
}
_dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY;
}
}
}
}
@ -1735,14 +1749,20 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
if (getShapeType() == SHAPE_TYPE_STATIC_MESH) {
setLocalAngularVelocity(Vectors::ZERO);
} else {
const float MIN_ANGULAR_SPEED = 0.0002f;
if (glm::length(value) < MIN_ANGULAR_SPEED) {
angularVelocity = ENTITY_ITEM_ZERO_VEC3;
} else {
angularVelocity = value;
float speed = glm::length(value);
if (!glm::isnan(speed)) {
const float MIN_ANGULAR_SPEED = 0.0002f;
const float MAX_ANGULAR_SPEED = 9.0f * TWO_PI; // 1/10 rotation per step at 90Hz
if (speed < MIN_ANGULAR_SPEED) {
angularVelocity = ENTITY_ITEM_ZERO_VEC3;
} else if (speed > MAX_ANGULAR_SPEED) {
angularVelocity = (MAX_ANGULAR_SPEED / speed) * value;
} else {
angularVelocity = value;
}
setLocalAngularVelocity(angularVelocity);
_dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY;
}
setLocalAngularVelocity(angularVelocity);
_dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY;
}
}
}

View file

@ -210,7 +210,16 @@ PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const {
auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face);
if (faceSize != 0 && faceOffset != 0) {
auto file = maybeOpenFile();
result = file->createView(faceSize, faceOffset)->toMemoryStorage();
if (file) {
auto storageView = file->createView(faceSize, faceOffset);
if (storageView) {
return storageView->toMemoryStorage();
} else {
qWarning() << "Failed to get a valid storageView for faceSize=" << faceSize << " faceOffset=" << faceOffset << "out of valid file " << QString::fromStdString(_filename);
}
} else {
qWarning() << "Failed to get a valid file out of maybeOpenFile " << QString::fromStdString(_filename);
}
}
return result;
}
@ -238,8 +247,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
throw std::runtime_error("Invalid level");
}
if (storage->size() != _ktxDescriptor->images[level]._imageSize) {
qWarning() << "Invalid image size: " << storage->size() << ", expected: " << _ktxDescriptor->images[level]._imageSize
auto& imageDesc = _ktxDescriptor->images[level];
if (storage->size() != imageDesc._imageSize) {
qWarning() << "Invalid image size: " << storage->size() << ", expected: " << imageDesc._imageSize
<< ", level: " << level << ", filename: " << QString::fromStdString(_filename);
return;
}
@ -258,7 +268,7 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
return;
}
memcpy(imageData, storage->data(), _ktxDescriptor->images[level]._imageSize);
memcpy(imageData, storage->data(), storage->size());
_minMipLevelAvailable = level;
if (_offsetToMinMipKV > 0) {
auto minMipKeyData = file->mutableData() + ktx::KTX_HEADER_SIZE + _offsetToMinMipKV;

View file

@ -38,15 +38,15 @@ UInt32 numberOfArrayElements
UInt32 numberOfFaces
UInt32 numberOfMipmapLevels
UInt32 bytesOfKeyValueData
for each keyValuePair that fits in bytesOfKeyValueData
UInt32 keyAndValueByteSize
Byte keyAndValue[keyAndValueByteSize]
Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)]
end
for each mipmap_level in numberOfMipmapLevels*
UInt32 imageSize;
UInt32 imageSize;
for each array_element in numberOfArrayElements*
for each face in numberOfFaces
for each z_slice in pixelDepth*
@ -269,7 +269,7 @@ namespace ktx {
COMPRESSED_RG11_EAC = 0x9272,
COMPRESSED_SIGNED_RG11_EAC = 0x9273,
};
enum class GLBaseInternalFormat : uint32_t {
// GL 4.4 Table 8.11
DEPTH_COMPONENT = 0x1902,
@ -419,9 +419,9 @@ namespace ktx {
using FaceOffsets = std::vector<size_t>;
using FaceBytes = std::vector<const Byte*>;
const uint32_t _numFaces;
// This is the byte offset from the _start_ of the image region. For example, level 0
// will have a byte offset of 0.
const uint32_t _numFaces;
const size_t _imageOffset;
const uint32_t _imageSize;
const uint32_t _faceSize;
@ -466,7 +466,7 @@ namespace ktx {
class KTX;
// A KTX descriptor is a lightweight container for all the information about a serialized KTX file, but without the
// A KTX descriptor is a lightweight container for all the information about a serialized KTX file, but without the
// actual image / face data available.
struct KTXDescriptor {
KTXDescriptor(const Header& header, const KeyValues& keyValues, const ImageDescriptors& imageDescriptors) : header(header), keyValues(keyValues), images(imageDescriptors) {}
@ -495,7 +495,7 @@ namespace ktx {
// Instead of creating a full Copy of the src data in a KTX object, the write serialization can be performed with the
// following two functions
// size_t sizeNeeded = KTX::evalStorageSize(header, images);
//
//
// //allocate a buffer of size "sizeNeeded" or map a file with enough capacity
// Byte* destBytes = new Byte[sizeNeeded];
//

View file

@ -127,6 +127,7 @@ namespace ktx {
size_t KTX::writeWithoutImages(Byte* destBytes, size_t destByteSize, const Header& header, const ImageDescriptors& descriptors, const KeyValues& keyValues) {
// Check again that we have enough destination capacity
if (!destBytes || (destByteSize < evalStorageSize(header, descriptors, keyValues))) {
qWarning() << "Destination capacity is insufficient to write KTX without images";
return 0;
}
@ -149,13 +150,15 @@ namespace ktx {
for (size_t i = 0; i < descriptors.size(); ++i) {
auto ptr = reinterpret_cast<uint32_t*>(currentDestPtr);
*ptr = descriptors[i]._imageSize;
ptr++;
#ifdef DEBUG
ptr++;
for (size_t k = 0; k < descriptors[i]._imageSize/4; k++) {
*(ptr + k) = 0xFFFFFFFF;
}
#endif
currentDestPtr += descriptors[i]._imageSize + sizeof(uint32_t);
currentDestPtr += sizeof(uint32_t);
currentDestPtr += descriptors[i]._imageSize;
}
return destByteSize;

View file

@ -501,12 +501,19 @@ void NetworkTexture::ktxMipRequestFinished() {
if (texture) {
texture->assignStoredMip(_ktxMipLevelRangeInFlight.first,
_ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data()));
_lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel();
if (texture->minAvailableMipLevel() <= _ktxMipLevelRangeInFlight.first) {
_lowestKnownPopulatedMip = texture->minAvailableMipLevel();
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
} else {
qWarning(networking) << "Failed to load mip: " << _url << ":" << _ktxMipLevelRangeInFlight.first;
_ktxResourceState = FAILED_TO_LOAD;
}
} else {
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
qWarning(networking) << "Trying to update mips but texture is null";
}
finishedLoading(true);
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
} else {
finishedLoading(false);
if (handleFailedRequest(_ktxMipRequest->getResult())) {

View file

@ -112,6 +112,9 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
_dynamicsWorld = nullptr;
}
int16_t collisionGroup = computeCollisionGroup();
if (_rigidBody) {
updateMassProperties();
}
if (world && _rigidBody) {
// add to new world
_dynamicsWorld = world;
@ -127,7 +130,9 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
_ghost.setCollisionGroupAndMask(collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ collisionGroup));
_ghost.setCollisionWorld(_dynamicsWorld);
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
if (_rigidBody) {
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
}
}
if (_dynamicsWorld) {
if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) {

View file

@ -128,6 +128,7 @@ protected:
void setState(State state);
#endif
virtual void updateMassProperties() = 0;
void updateGravity();
void updateUpAxis(const glm::quat& rotation);
bool checkForSupport(btCollisionWorld* collisionWorld);

View file

@ -1,378 +0,0 @@
//
// ObjectActionSpring.cpp
// libraries/physics/src
//
// Created by Seth Alves 2015-6-5
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "QVariantGLM.h"
#include "ObjectActionSpring.h"
#include "PhysicsLogging.h"
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;
ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectAction(DYNAMIC_TYPE_SPRING, id, ownerEntity),
_positionalTarget(glm::vec3(0.0f)),
_desiredPositionalTarget(glm::vec3(0.0f)),
_linearTimeScale(FLT_MAX),
_positionalTargetSet(true),
_rotationalTarget(glm::quat()),
_desiredRotationalTarget(glm::quat()),
_angularTimeScale(FLT_MAX),
_rotationalTargetSet(true) {
#if WANT_DEBUG
qCDebug(physics) << "ObjectActionSpring::ObjectActionSpring";
#endif
}
ObjectActionSpring::~ObjectActionSpring() {
#if WANT_DEBUG
qCDebug(physics) << "ObjectActionSpring::~ObjectActionSpring";
#endif
}
bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
float& linearTimeScale, float& angularTimeScale) {
SpatiallyNestablePointer other = getOther();
withReadLock([&]{
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;
}
bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) {
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
return false;
}
glm::quat rotation;
glm::vec3 position;
glm::vec3 linearVelocity;
glm::vec3 angularVelocity;
bool linearValid = false;
int linearSpringCount = 0;
bool angularValid = false;
int angularSpringCount = 0;
QList<EntityDynamicPointer> springDerivedActions;
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_SPRING));
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_FAR_GRAB));
springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_HOLD));
foreach (EntityDynamicPointer action, springDerivedActions) {
std::shared_ptr<ObjectActionSpring> springAction = std::static_pointer_cast<ObjectActionSpring>(action);
glm::quat rotationForAction;
glm::vec3 positionForAction;
glm::vec3 linearVelocityForAction;
glm::vec3 angularVelocityForAction;
float linearTimeScale;
float angularTimeScale;
bool success = springAction->getTarget(deltaTimeStep,
rotationForAction, positionForAction,
linearVelocityForAction, angularVelocityForAction,
linearTimeScale, angularTimeScale);
if (success) {
if (angularTimeScale < MAX_SPRING_TIMESCALE) {
angularValid = true;
angularSpringCount++;
angularVelocity += angularVelocityForAction;
if (springAction.get() == this) {
// only use the rotation for this action
rotation = rotationForAction;
}
}
if (linearTimeScale < MAX_SPRING_TIMESCALE) {
linearValid = true;
linearSpringCount++;
position += positionForAction;
linearVelocity += linearVelocityForAction;
}
}
}
if ((angularValid && angularSpringCount > 0) || (linearValid && linearSpringCount > 0)) {
withWriteLock([&]{
if (linearValid && linearSpringCount > 0) {
position /= linearSpringCount;
linearVelocity /= linearSpringCount;
_positionalTarget = position;
_linearVelocityTarget = linearVelocity;
_positionalTargetSet = true;
_active = true;
}
if (angularValid && angularSpringCount > 0) {
angularVelocity /= angularSpringCount;
_rotationalTarget = rotation;
_angularVelocityTarget = angularVelocity;
_rotationalTargetSet = true;
_active = true;
}
});
}
return linearValid || angularValid;
}
void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
if (!prepareForSpringUpdate(deltaTimeStep)) {
return;
}
withReadLock([&]{
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
return;
}
void* physicsInfo = ownerEntity->getPhysicsInfo();
if (!physicsInfo) {
return;
}
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
btRigidBody* rigidBody = motionState->getRigidBody();
if (!rigidBody) {
qCDebug(physics) << "ObjectActionSpring::updateActionWorker no rigidBody";
return;
}
if (_linearTimeScale < MAX_SPRING_TIMESCALE) {
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget);
float offsetLength = offset.length();
if (offsetLength > FLT_EPSILON) {
float speed = glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED);
targetVelocity = (-speed / offsetLength) * offset;
if (speed > rigidBody->getLinearSleepingThreshold()) {
forceBodyNonStatic();
rigidBody->activate();
}
}
// this action is aggresively critically damped and defeats the current velocity
rigidBody->setLinearVelocity(targetVelocity);
}
if (_angularTimeScale < MAX_SPRING_TIMESCALE) {
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
btQuaternion bodyRotation = rigidBody->getOrientation();
auto alignmentDot = bodyRotation.dot(glmToBullet(_rotationalTarget));
const float ALMOST_ONE = 0.99999f;
if (glm::abs(alignmentDot) < ALMOST_ONE) {
btQuaternion target = glmToBullet(_rotationalTarget);
if (alignmentDot < 0.0f) {
target = -target;
}
// if dQ is the incremental rotation that gets an object from Q0 to Q1 then:
//
// Q1 = dQ * Q0
//
// solving for dQ gives:
//
// dQ = Q1 * Q0^
btQuaternion deltaQ = target * bodyRotation.inverse();
float speed = deltaQ.getAngle() / _angularTimeScale;
targetVelocity = speed * deltaQ.getAxis();
if (speed > rigidBody->getAngularSleepingThreshold()) {
rigidBody->activate();
}
}
// this action is aggresively critically damped and defeats the current velocity
rigidBody->setAngularVelocity(targetVelocity);
}
});
}
const float MIN_TIMESCALE = 0.1f;
bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
glm::vec3 positionalTarget;
float linearTimeScale;
glm::quat rotationalTarget;
float angularTimeScale;
QUuid otherID;
bool needUpdate = false;
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
withReadLock([&]{
// targets are required, spring-constants are optional
bool ok = true;
positionalTarget = EntityDynamicInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
if (!ok) {
positionalTarget = _desiredPositionalTarget;
}
ok = true;
linearTimeScale = EntityDynamicInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
if (!ok || linearTimeScale <= 0.0f) {
linearTimeScale = _linearTimeScale;
}
ok = true;
rotationalTarget = EntityDynamicInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
if (!ok) {
rotationalTarget = _desiredRotationalTarget;
}
ok = true;
angularTimeScale =
EntityDynamicInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false);
if (!ok) {
angularTimeScale = _angularTimeScale;
}
ok = true;
otherID = QUuid(EntityDynamicInterface::extractStringArgument("spring action",
arguments, "otherID", ok, false));
if (!ok) {
otherID = _otherID;
}
if (somethingChanged ||
positionalTarget != _desiredPositionalTarget ||
linearTimeScale != _linearTimeScale ||
rotationalTarget != _desiredRotationalTarget ||
angularTimeScale != _angularTimeScale ||
otherID != _otherID) {
// something changed
needUpdate = true;
}
});
if (needUpdate) {
withWriteLock([&] {
_desiredPositionalTarget = positionalTarget;
_linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale));
_desiredRotationalTarget = rotationalTarget;
_angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
_otherID = otherID;
_active = true;
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setDynamicDataDirty(true);
ownerEntity->setDynamicDataNeedsTransmit(true);
}
});
activateBody();
}
return true;
}
QVariantMap ObjectActionSpring::getArguments() {
QVariantMap arguments = ObjectDynamic::getArguments();
withReadLock([&] {
arguments["linearTimeScale"] = _linearTimeScale;
arguments["targetPosition"] = glmToQMap(_desiredPositionalTarget);
arguments["targetRotation"] = glmToQMap(_desiredRotationalTarget);
arguments["angularTimeScale"] = _angularTimeScale;
arguments["otherID"] = _otherID;
});
return arguments;
}
void ObjectActionSpring::serializeParameters(QDataStream& dataStream) const {
withReadLock([&] {
dataStream << _desiredPositionalTarget;
dataStream << _linearTimeScale;
dataStream << _positionalTargetSet;
dataStream << _desiredRotationalTarget;
dataStream << _angularTimeScale;
dataStream << _rotationalTargetSet;
dataStream << localTimeToServerTime(_expires);
dataStream << _tag;
dataStream << _otherID;
});
}
QByteArray ObjectActionSpring::serialize() const {
QByteArray serializedActionArguments;
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
dataStream << DYNAMIC_TYPE_SPRING;
dataStream << getID();
dataStream << ObjectActionSpring::springVersion;
serializeParameters(dataStream);
return serializedActionArguments;
}
void ObjectActionSpring::deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream) {
withWriteLock([&] {
dataStream >> _desiredPositionalTarget;
dataStream >> _linearTimeScale;
dataStream >> _positionalTargetSet;
dataStream >> _desiredRotationalTarget;
dataStream >> _angularTimeScale;
dataStream >> _rotationalTargetSet;
quint64 serverExpires;
dataStream >> serverExpires;
_expires = serverTimeToLocalTime(serverExpires);
dataStream >> _tag;
dataStream >> _otherID;
_active = true;
});
}
void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
QDataStream dataStream(serializedArguments);
EntityDynamicType type;
dataStream >> type;
assert(type == getType());
QUuid id;
dataStream >> id;
assert(id == getID());
uint16_t serializationVersion;
dataStream >> serializationVersion;
if (serializationVersion != ObjectActionSpring::springVersion) {
assert(false);
return;
}
deserializeParameters(serializedArguments, dataStream);
}

View file

@ -1,56 +0,0 @@
//
// ObjectActionSpring.h
// libraries/physics/src
//
// Created by Seth Alves 2015-6-5
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_ObjectActionSpring_h
#define hifi_ObjectActionSpring_h
#include "ObjectAction.h"
class ObjectActionSpring : public ObjectAction {
public:
ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity);
virtual ~ObjectActionSpring();
virtual bool updateArguments(QVariantMap arguments) override;
virtual QVariantMap getArguments() override;
virtual void updateActionWorker(float deltaTimeStep) override;
virtual QByteArray serialize() const override;
virtual void deserialize(QByteArray serializedArguments) override;
virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity,
float& linearTimeScale, float& angularTimeScale);
protected:
static const uint16_t springVersion;
glm::vec3 _positionalTarget;
glm::vec3 _desiredPositionalTarget;
float _linearTimeScale;
bool _positionalTargetSet;
glm::quat _rotationalTarget;
glm::quat _desiredRotationalTarget;
float _angularTimeScale;
bool _rotationalTargetSet;
glm::vec3 _linearVelocityTarget;
glm::vec3 _angularVelocityTarget;
virtual bool prepareForSpringUpdate(btScalar deltaTimeStep);
void serializeParameters(QDataStream& dataStream) const;
void deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream);
};
#endif // hifi_ObjectActionSpring_h

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <LogHandler.h>
#include "QVariantGLM.h"
#include "EntityTree.h"
@ -83,6 +85,9 @@ btTypedConstraint* ObjectConstraintBallSocket::getConstraint() {
return constraint;
}
static QString repeatedBallSocketNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex(
"ObjectConstraintBallSocket::getConstraint -- no rigidBody.*");
btRigidBody* rigidBodyA = getRigidBody();
if (!rigidBodyA) {
qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyA";
@ -94,6 +99,7 @@ btTypedConstraint* ObjectConstraintBallSocket::getConstraint() {
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
if (!rigidBodyB) {
qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyB";
return nullptr;
}

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <LogHandler.h>
#include "QVariantGLM.h"
#include "EntityTree.h"
@ -94,6 +96,9 @@ btTypedConstraint* ObjectConstraintConeTwist::getConstraint() {
return constraint;
}
static QString repeatedConeTwistNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex(
"ObjectConstraintConeTwist::getConstraint -- no rigidBody.*");
btRigidBody* rigidBodyA = getRigidBody();
if (!rigidBodyA) {
qCDebug(physics) << "ObjectConstraintConeTwist::getConstraint -- no rigidBodyA";
@ -125,6 +130,7 @@ btTypedConstraint* ObjectConstraintConeTwist::getConstraint() {
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
if (!rigidBodyB) {
qCDebug(physics) << "ObjectConstraintConeTwist::getConstraint -- no rigidBodyB";
return nullptr;
}

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <LogHandler.h>
#include "QVariantGLM.h"
#include "EntityTree.h"
@ -93,6 +95,9 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() {
return constraint;
}
static QString repeatedHingeNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex(
"ObjectConstraintHinge::getConstraint -- no rigidBody.*");
btRigidBody* rigidBodyA = getRigidBody();
if (!rigidBodyA) {
qCDebug(physics) << "ObjectConstraintHinge::getConstraint -- no rigidBodyA";
@ -110,6 +115,7 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() {
// This hinge is between two entities... find the other rigid body.
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
if (!rigidBodyB) {
qCDebug(physics) << "ObjectConstraintHinge::getConstraint -- no rigidBodyB";
return nullptr;
}

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <LogHandler.h>
#include "QVariantGLM.h"
#include "EntityTree.h"
@ -85,6 +87,9 @@ btTypedConstraint* ObjectConstraintSlider::getConstraint() {
return constraint;
}
static QString repeatedSliderNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex(
"ObjectConstraintSlider::getConstraint -- no rigidBody.*");
btRigidBody* rigidBodyA = getRigidBody();
if (!rigidBodyA) {
qCDebug(physics) << "ObjectConstraintSlider::getConstraint -- no rigidBodyA";
@ -116,6 +121,7 @@ btTypedConstraint* ObjectConstraintSlider::getConstraint() {
btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID);
if (!rigidBodyB) {
qCDebug(physics) << "ObjectConstraintSlider::getConstraint -- no rigidBodyB";
return nullptr;
}

View file

@ -160,56 +160,6 @@ btRigidBody* ObjectDynamic::getRigidBody() {
return nullptr;
}
glm::vec3 ObjectDynamic::getPosition() {
auto rigidBody = getRigidBody();
if (!rigidBody) {
return glm::vec3(0.0f);
}
return bulletToGLM(rigidBody->getCenterOfMassPosition());
}
glm::quat ObjectDynamic::getRotation() {
auto rigidBody = getRigidBody();
if (!rigidBody) {
return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
}
return bulletToGLM(rigidBody->getOrientation());
}
glm::vec3 ObjectDynamic::getLinearVelocity() {
auto rigidBody = getRigidBody();
if (!rigidBody) {
return glm::vec3(0.0f);
}
return bulletToGLM(rigidBody->getLinearVelocity());
}
void ObjectDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
auto rigidBody = getRigidBody();
if (!rigidBody) {
return;
}
rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
rigidBody->activate();
}
glm::vec3 ObjectDynamic::getAngularVelocity() {
auto rigidBody = getRigidBody();
if (!rigidBody) {
return glm::vec3(0.0f);
}
return bulletToGLM(rigidBody->getAngularVelocity());
}
void ObjectDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
auto rigidBody = getRigidBody();
if (!rigidBody) {
return;
}
rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
rigidBody->activate();
}
void ObjectDynamic::activateBody(bool forceActivation) {
auto rigidBody = getRigidBody();
if (rigidBody) {

View file

@ -56,12 +56,6 @@ protected:
btRigidBody* getOtherRigidBody(EntityItemID otherEntityID);
EntityItemPointer getEntityByID(EntityItemID entityID) const;
virtual btRigidBody* getRigidBody();
virtual glm::vec3 getPosition() override;
virtual glm::quat getRotation() override;
virtual glm::vec3 getLinearVelocity() override;
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
virtual glm::vec3 getAngularVelocity() override;
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
virtual void activateBody(bool forceActivation = false);
virtual void forceBodyNonStatic();

View file

@ -63,10 +63,7 @@ ShapeManager* ObjectMotionState::getShapeManager() {
}
ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) :
_motionType(MOTION_TYPE_STATIC),
_shape(shape),
_body(nullptr),
_mass(0.0f),
_lastKinematicStep(worldSimulationStep)
{
}
@ -74,7 +71,43 @@ ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) :
ObjectMotionState::~ObjectMotionState() {
assert(!_body);
setShape(nullptr);
_type = MOTIONSTATE_TYPE_INVALID;
}
void ObjectMotionState::setMass(float mass) {
_density = 1.0f;
if (_shape) {
// we compute the density for the current shape's Aabb volume
// and save that instead of the total mass
btTransform transform;
transform.setIdentity();
btVector3 minCorner, maxCorner;
_shape->getAabb(transform, minCorner, maxCorner);
btVector3 diagonal = maxCorner - minCorner;
float volume = diagonal.getX() * diagonal.getY() * diagonal.getZ();
if (volume > EPSILON) {
_density = fabsf(mass) / volume;
}
}
}
float ObjectMotionState::getMass() const {
if (_shape) {
// scale the density by the current Aabb volume to get mass
btTransform transform;
transform.setIdentity();
btVector3 minCorner, maxCorner;
_shape->getAabb(transform, minCorner, maxCorner);
btVector3 diagonal = maxCorner - minCorner;
float volume = diagonal.getX() * diagonal.getY() * diagonal.getZ();
// cap the max mass for numerical stability
const float MIN_OBJECT_MASS = 0.0f;
const float MAX_OBJECT_DENSITY = 20000.0f; // kg/m^3 density of Tungsten
const float MAX_OBJECT_VOLUME = 1.0e6f;
const float MAX_OBJECT_MASS = MAX_OBJECT_DENSITY * MAX_OBJECT_VOLUME;
return glm::clamp(_density * volume, MIN_OBJECT_MASS, MAX_OBJECT_MASS);
}
return 0.0f;
}
void ObjectMotionState::setBodyLinearVelocity(const glm::vec3& velocity) const {

View file

@ -93,8 +93,8 @@ public:
MotionStateType getType() const { return _type; }
virtual PhysicsMotionType getMotionType() const { return _motionType; }
void setMass(float mass) { _mass = fabsf(mass); }
float getMass() { return _mass; }
void setMass(float mass);
float getMass() const;
void setBodyLinearVelocity(const glm::vec3& velocity) const;
void setBodyAngularVelocity(const glm::vec3& velocity) const;
@ -159,12 +159,12 @@ protected:
void setRigidBody(btRigidBody* body);
virtual void setShape(const btCollisionShape* shape);
MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState
PhysicsMotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC
MotionStateType _type { MOTIONSTATE_TYPE_INVALID }; // type of MotionState
PhysicsMotionType _motionType { MOTION_TYPE_STATIC }; // type of motion: KINEMATIC, DYNAMIC, or STATIC
const btCollisionShape* _shape;
btRigidBody* _body;
float _mass;
btRigidBody* _body { nullptr };
float _density { 1.0f };
uint32_t _lastKinematicStep;
bool _hasInternalKinematicChanges { false };

View file

@ -126,19 +126,8 @@ QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) {
}
switch (variantType) {
case QVariant::Map: {
auto varmap = variant.toMap();
for (auto mapit = varmap.cbegin(); mapit != varmap.cend(); ++mapit) {
auto& mapkey = mapit.key();
auto& mapvariant = mapit.value();
object.insert(key + "/" + mapkey, QJsonValue::fromVariant(mapvariant));
}
break;
}
case QVariant::List:
case QVariant::Hash: {
qCritical() << "Unsupported variant type" << variant.typeName();
qCritical() << "Unsupported variant type" << variant.typeName() << ";" << key << variant;
Q_ASSERT(false);
break;
}
@ -152,6 +141,8 @@ QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map) {
case QVariant::UInt:
case QVariant::Bool:
case QVariant::Double:
case QVariant::Map:
case QVariant::List:
object.insert(key, QJsonValue::fromVariant(variant));
break;

View file

@ -768,9 +768,10 @@ bool similarStrings(const QString& stringA, const QString& stringB) {
}
void disableQtBearerPoll() {
// to work around the Qt constant wireless scanning, set the env for polling interval very high
const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT16_MAX).toLocal8Bit();
qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
// to disable the Qt constant wireless scanning, set the env for polling interval
qDebug() << "Disabling Qt wireless polling by using a negative value for QTimer::setInterval";
const QByteArray DISABLE_BEARER_POLL_TIMEOUT = QString::number(-1).toLocal8Bit();
qputenv("QT_BEARER_POLL_TIMEOUT", DISABLE_BEARER_POLL_TIMEOUT);
}
void printSystemInformation() {

View file

@ -25,7 +25,9 @@ StoragePointer Storage::createView(size_t viewSize, size_t offset) const {
viewSize = selfSize;
}
if ((viewSize + offset) > selfSize) {
throw std::runtime_error("Invalid mapping range");
return StoragePointer();
//TODO: Disable te exception for now and return an empty storage instead.
//throw std::runtime_error("Invalid mapping range");
}
return std::make_shared<ViewStorage>(shared_from_this(), viewSize, data() + offset);
}

View file

@ -1,6 +1,6 @@
// shopItemGrab.js
//
// Semplified and coarse version of handControllerGrab.js with the addition of the ownerID concept.
// Simplified and coarse version of handControllerGrab.js with the addition of the ownerID concept.
// This grab is the only one which should run in the vrShop. It allows only near grab and add the feature of checking the ownerID. (See shopGrapSwapperEntityScript.js)
//
@ -63,8 +63,8 @@ var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-g
// equip
//
var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05;
var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position
var EQUIP_TRACTOR_SHUTOFF_DISTANCE = 0.05;
var EQUIP_TRACTOR_TIMEFRAME = 0.4; // how quickly objects move to their new position
//
// other constants
@ -121,7 +121,7 @@ var STATE_EQUIP_SEARCHING = 11;
var STATE_EQUIP = 12
var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down
var STATE_CONTINUE_EQUIP = 14;
var STATE_EQUIP_SPRING = 16;
var STATE_EQUIP_TRACTOR = 16;
function stateToName(state) {
@ -152,8 +152,8 @@ function stateToName(state) {
return "continue_equip_bd";
case STATE_CONTINUE_EQUIP:
return "continue_equip";
case STATE_EQUIP_SPRING:
return "state_equip_spring";
case STATE_EQUIP_TRACTOR:
return "state_equip_tractor";
}
return "unknown";
@ -216,7 +216,7 @@ function MyController(hand) {
case STATE_EQUIP:
this.nearGrabbing();
break;
case STATE_EQUIP_SPRING:
case STATE_EQUIP_TRACTOR:
this.pullTowardEquipPosition()
break;
case STATE_CONTINUE_NEAR_GRABBING:
@ -404,14 +404,14 @@ function MyController(hand) {
return;
} else if (!properties.locked) {
var ownerObj = getEntityCustomData('ownerKey', intersection.entityID, null);
if (ownerObj == null || ownerObj.ownerID === MyAvatar.sessionUUID) { //I can only grab new or already mine items
this.grabbedEntity = intersection.entityID;
if (this.state == STATE_SEARCHING) {
this.setState(STATE_NEAR_GRABBING);
} else { // equipping
if (typeof grabbableData.spatialKey !== 'undefined') {
this.setState(STATE_EQUIP_SPRING);
this.setState(STATE_EQUIP_TRACTOR);
} else {
this.setState(STATE_EQUIP);
}
@ -558,7 +558,7 @@ function MyController(hand) {
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
// use a spring to pull the object to where it will be when equipped
// use a tractor to pull the object to where it will be when equipped
var relativeRotation = {
x: 0.0,
y: 0.0,
@ -582,34 +582,34 @@ function MyController(hand) {
var offset = Vec3.multiplyQbyV(targetRotation, relativePosition);
var targetPosition = Vec3.sum(handPosition, offset);
if (typeof this.equipSpringID === 'undefined' ||
this.equipSpringID === null ||
this.equipSpringID === NULL_ACTION_ID) {
this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, {
if (typeof this.equipTractorID === 'undefined' ||
this.equipTractorID === null ||
this.equipTractorID === NULL_ACTION_ID) {
this.equipTractorID = Entities.addAction("tractor", this.grabbedEntity, {
targetPosition: targetPosition,
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
linearTimeScale: EQUIP_TRACTOR_TIMEFRAME,
targetRotation: targetRotation,
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
angularTimeScale: EQUIP_TRACTOR_TIMEFRAME,
ttl: ACTION_TTL
});
if (this.equipSpringID === NULL_ACTION_ID) {
this.equipSpringID = null;
if (this.equipTractorID === NULL_ACTION_ID) {
this.equipTractorID = null;
this.setState(STATE_OFF);
return;
}
} else {
Entities.updateAction(this.grabbedEntity, this.equipSpringID, {
Entities.updateAction(this.grabbedEntity, this.equipTractorID, {
targetPosition: targetPosition,
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
linearTimeScale: EQUIP_TRACTOR_TIMEFRAME,
targetRotation: targetRotation,
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
angularTimeScale: EQUIP_TRACTOR_TIMEFRAME,
ttl: ACTION_TTL
});
}
if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) {
Entities.deleteAction(this.grabbedEntity, this.equipSpringID);
this.equipSpringID = null;
if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_TRACTOR_SHUTOFF_DISTANCE) {
Entities.deleteAction(this.grabbedEntity, this.equipTractorID);
this.equipTractorID = null;
this.setState(STATE_EQUIP);
}
};
@ -862,4 +862,4 @@ function cleanup() {
}
Script.scriptEnding.connect(cleanup);
Script.update.connect(update);
Script.update.connect(update);

View file

@ -9,8 +9,8 @@
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.
<input type="button" id="cone-twist-and-tractor-lever-test" value="Cone-Twist and Tractor-Action Lever"><br>
A platform with a lever. The lever can be moved in a cone and rotated. A tractor 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.
@ -31,7 +31,7 @@
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.
A self-righting ragdoll. The head is on a weak tractor vs the body.
</body>
</html>

View file

@ -28,7 +28,7 @@
function coneTwistAndSpringLeverTest(params) {
function coneTwistAndTractorLeverTest(params) {
var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: -0.5, z: -2}));
var lifetime = params.lifetime;
@ -84,10 +84,10 @@
tag: "cone-twist test"
});
Entities.addAction("spring", leverID, {
Entities.addAction("tractor", leverID, {
targetRotation: { x: 0, y: 0, z: 0, w: 1 },
angularTimeScale: 0.2,
tag: "cone-twist test spring"
tag: "cone-twist test tractor"
});
@ -349,11 +349,11 @@
userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }"
});
Entities.addAction("spring", headID, {
Entities.addAction("tractor", headID, {
targetRotation: { x: 0, y: 0, z: 0, w: 1 },
angularTimeScale: 2.0,
otherID: bodyID,
tag: "cone-twist test spring"
tag: "cone-twist test tractor"
});
@ -705,7 +705,7 @@
if (event["dynamics-tests-command"]) {
var commandToFunctionMap = {
"cone-twist-and-spring-lever-test": coneTwistAndSpringLeverTest,
"cone-twist-and-tractor-lever-test": coneTwistAndTractorLeverTest,
"door-vs-world-test": doorVSWorldTest,
"hinge-chain-test": hingeChainTest,
"slider-vs-world-test": sliderVSWorldTest,

View file

@ -113,4 +113,4 @@
};
return new SpringHold();
});
});

View file

@ -2,7 +2,7 @@
// touch.js
//
// Sample file using spring action, haptic vibration, and color change to demonstrate two spheres
// Sample file using tractor action, haptic vibration, and color change to demonstrate two spheres
// That can give a sense of touch to the holders.
// Create two standard spheres, make them grabbable, and attach this entity script. Grab them and touch them together.
//
@ -53,7 +53,7 @@
_this = this;
}
function updateSpringAction(timescale) {
function updateTractorAction(timescale) {
var targetProps = Entities.getEntityProperties(_this.entityID);
//
// Look for nearby entities to touch
@ -113,7 +113,7 @@
var success = Entities.updateAction(_this.copy, _this.actionID, props);
}
function createSpringAction(timescale) {
function createTractorAction(timescale) {
var targetProps = Entities.getEntityProperties(_this.entityID);
var props = {
@ -123,7 +123,7 @@
angularTimeScale: timescale,
ttl: ACTION_TTL
};
_this.actionID = Entities.addAction("spring", _this.copy, props);
_this.actionID = Entities.addAction("tractor", _this.copy, props);
return;
}
@ -170,7 +170,7 @@
});
}
function deleteSpringAction() {
function deleteTractorAction() {
Entities.deleteAction(_this.copy, _this.actionID);
}
@ -188,19 +188,19 @@
},
startNearGrab: function(entityID, data) {
createCopy();
createSpringAction(TIMESCALE);
createTractorAction(TIMESCALE);
makeOriginalInvisible();
setHand(Entities.getEntityProperties(_this.entityID).position);
},
continueNearGrab: function() {
updateSpringAction(TIMESCALE);
updateTractorAction(TIMESCALE);
},
releaseGrab: function() {
deleteSpringAction();
deleteTractorAction();
deleteCopy();
makeOriginalVisible();
}
};
return new TouchExample();
});
});

View file

@ -89,7 +89,7 @@ LookAtTarget = function(sourceEntityID) {
}
});
if (!actionFound) {
Entities.addAction('spring', _sourceEntityID, getNewActionProperties());
Entities.addAction('tractor', _sourceEntityID, getNewActionProperties());
}
}
};