mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into brown
This commit is contained in:
commit
d9ad45bf4a
51 changed files with 455 additions and 753 deletions
|
@ -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.";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -239,6 +239,7 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
|
|||
});
|
||||
|
||||
connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete()));
|
||||
_characterController.setDensity(_density);
|
||||
}
|
||||
|
||||
MyAvatar::~MyAvatar() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -113,4 +113,4 @@
|
|||
};
|
||||
|
||||
return new SpringHold();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -89,7 +89,7 @@ LookAtTarget = function(sourceEntityID) {
|
|||
}
|
||||
});
|
||||
if (!actionFound) {
|
||||
Entities.addAction('spring', _sourceEntityID, getNewActionProperties());
|
||||
Entities.addAction('tractor', _sourceEntityID, getNewActionProperties());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue