Merge pull request #4702 from AndrewMeadows/thermonuclear

PhysicsEngine cleanup in preparation for colliding avatars
This commit is contained in:
Seth Alves 2015-04-27 16:20:27 -07:00
commit e48552f243
19 changed files with 178 additions and 143 deletions

View file

@ -4425,7 +4425,6 @@ void Application::checkSkeleton() {
_myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default"); _myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default");
} else { } else {
_myAvatar->updateCharacterController();
_physicsEngine.setCharacterController(_myAvatar->getCharacterController()); _physicsEngine.setCharacterController(_myAvatar->getCharacterController());
} }
} }

View file

@ -1058,3 +1058,20 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
} }
// virtual
void Avatar::rebuildSkeletonBody() {
/* TODO: implement this and remove override from MyAvatar (when we have AvatarMotionStates working)
if (_motionState) {
// compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius();
float height = 2.0f * (capsule.getHalfHeight() + radius);
glm::vec3 corner(-radius, -0.5f * height, -radius);
corner += _skeletonModel.getBoundingShapeOffset();
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
//_characterController.setLocalBoundingBox(corner, scale);
_motionState->setBoundingBox(corner, scale);
}
*/
}

View file

@ -162,6 +162,8 @@ public:
// (otherwise floating point error will cause problems at large positions). // (otherwise floating point error will cause problems at large positions).
void applyPositionDelta(const glm::vec3& delta); void applyPositionDelta(const glm::vec3& delta);
virtual void rebuildSkeletonBody();
signals: signals:
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
@ -228,6 +230,8 @@ private:
static int _jointConesID; static int _jointConesID;
int _voiceSphereID; int _voiceSphereID;
//AvatarMotionState* _motionState = nullptr;
}; };
#endif // hifi_Avatar_h #endif // hifi_Avatar_h

View file

@ -59,7 +59,7 @@ private:
AvatarSharedPointer newSharedAvatar(); AvatarSharedPointer newSharedAvatar();
// virtual override // virtual overrides
AvatarHash::iterator erase(const AvatarHash::iterator& iterator); AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
QVector<AvatarSharedPointer> _avatarFades; QVector<AvatarSharedPointer> _avatarFades;

View file

@ -1079,7 +1079,7 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
return Avatar::getPosition(); return Avatar::getPosition();
} }
void MyAvatar::updateCharacterController() { void MyAvatar::rebuildSkeletonBody() {
// compute localAABox // compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius(); float radius = capsule.getRadius();

View file

@ -138,7 +138,6 @@ public:
virtual glm::vec3 getSkeletonPosition() const; virtual glm::vec3 getSkeletonPosition() const;
void updateLocalAABox(); void updateLocalAABox();
DynamicCharacterController* getCharacterController() { return &_characterController; } DynamicCharacterController* getCharacterController() { return &_characterController; }
void updateCharacterController();
void clearJointAnimationPriorities(); void clearJointAnimationPriorities();
@ -192,6 +191,8 @@ public slots:
void stopRecording(); void stopRecording();
void saveRecording(QString filename); void saveRecording(QString filename);
void loadLastRecording(); void loadLastRecording();
virtual void rebuildSkeletonBody();
signals: signals:
void transformChanged(); void transformChanged();

View file

@ -40,13 +40,14 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
_clampedFootPosition(0.0f), _clampedFootPosition(0.0f),
_headClipDistance(DEFAULT_NEAR_CLIP) _headClipDistance(DEFAULT_NEAR_CLIP)
{ {
assert(_owningAvatar);
} }
SkeletonModel::~SkeletonModel() { SkeletonModel::~SkeletonModel() {
} }
void SkeletonModel::setJointStates(QVector<JointState> states) { void SkeletonModel::initJointStates(QVector<JointState> states) {
Model::setJointStates(states); Model::initJointStates(states);
// Determine the default eye position for avatar scale = 1.0 // Determine the default eye position for avatar scale = 1.0
int headJointIndex = _geometry->getFBXGeometry().headJointIndex; int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
@ -83,6 +84,7 @@ void SkeletonModel::setJointStates(QVector<JointState> states) {
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z); _headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
_headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP); _headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP);
_owningAvatar->rebuildSkeletonBody();
emit skeletonLoaded(); emit skeletonLoaded();
} }

View file

@ -28,7 +28,7 @@ public:
SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL); SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL);
~SkeletonModel(); ~SkeletonModel();
void setJointStates(QVector<JointState> states); virtual void initJointStates(QVector<JointState> states);
void simulate(float deltaTime, bool fullUpdate = true); void simulate(float deltaTime, bool fullUpdate = true);

View file

@ -680,6 +680,17 @@ void EntityItem::setMass(float mass) {
} }
} }
const float DEFAULT_ENTITY_RESTITUTION = 0.5f;
const float DEFAULT_ENTITY_FRICTION = 0.5f;
float EntityItem::getRestitution() const {
return DEFAULT_ENTITY_RESTITUTION;
}
float EntityItem::getFriction() const {
return DEFAULT_ENTITY_FRICTION;
}
void EntityItem::simulate(const quint64& now) { void EntityItem::simulate(const quint64& now) {
if (_lastSimulated == 0) { if (_lastSimulated == 0) {
_lastSimulated = now; _lastSimulated = now;
@ -840,11 +851,11 @@ glm::mat4 EntityItem::getWorldToEntityMatrix() const {
return glm::inverse(getEntityToWorldMatrix()); return glm::inverse(getEntityToWorldMatrix());
} }
glm::vec3 EntityItem::entityToWorld(const glm::vec3 point) const { glm::vec3 EntityItem::entityToWorld(const glm::vec3& point) const {
return glm::vec3(getEntityToWorldMatrix() * glm::vec4(point, 1.0f)); return glm::vec3(getEntityToWorldMatrix() * glm::vec4(point, 1.0f));
} }
glm::vec3 EntityItem::worldToEntity(const glm::vec3 point) const { glm::vec3 EntityItem::worldToEntity(const glm::vec3& point) const {
return glm::vec3(getWorldToEntityMatrix() * glm::vec4(point, 1.0f)); return glm::vec3(getWorldToEntityMatrix() * glm::vec4(point, 1.0f));
} }
@ -1164,7 +1175,7 @@ void EntityItem::updateVelocity(const glm::vec3& value) {
void EntityItem::updateDamping(float value) { void EntityItem::updateDamping(float value) {
if (fabsf(_damping - value) > MIN_DAMPING_DELTA) { if (fabsf(_damping - value) > MIN_DAMPING_DELTA) {
_damping = glm::clamp(value, 0.0f, 1.0f); _damping = glm::clamp(value, 0.0f, 1.0f);
_dirtyFlags |= EntityItem::DIRTY_VELOCITY; _dirtyFlags |= EntityItem::DIRTY_MATERIAL;
} }
} }
@ -1202,7 +1213,7 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
void EntityItem::updateAngularDamping(float value) { void EntityItem::updateAngularDamping(float value) {
if (fabsf(_angularDamping - value) > MIN_DAMPING_DELTA) { if (fabsf(_angularDamping - value) > MIN_DAMPING_DELTA) {
_angularDamping = glm::clamp(value, 0.0f, 1.0f); _angularDamping = glm::clamp(value, 0.0f, 1.0f);
_dirtyFlags |= EntityItem::DIRTY_VELOCITY; _dirtyFlags |= EntityItem::DIRTY_MATERIAL;
} }
} }

View file

@ -55,7 +55,8 @@ public:
DIRTY_SHAPE = 0x0020, DIRTY_SHAPE = 0x0020,
DIRTY_LIFETIME = 0x0040, DIRTY_LIFETIME = 0x0040,
DIRTY_UPDATEABLE = 0x0080, DIRTY_UPDATEABLE = 0x0080,
DIRTY_PHYSICS_NO_WAKE = 0x0100 // we want to update values in physics engine without "waking" the object up DIRTY_MATERIAL = 0x00100,
DIRTY_PHYSICS_NO_WAKE = 0x0200 // we want to update values in physics engine without "waking" the object up
}; };
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
@ -174,7 +175,7 @@ public:
float getDensity() const { return _density; } float getDensity() const { return _density; }
const glm::vec3 getVelocity() const { return _velocity; } /// get velocity in meters const glm::vec3& getVelocity() const { return _velocity; } /// get velocity in meters
void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters
bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; } bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; }
@ -182,13 +183,16 @@ public:
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters
bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; } bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; }
const glm::vec3 getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second const glm::vec3& getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second
void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second
bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; } bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; }
float getDamping() const { return _damping; } float getDamping() const { return _damping; }
void setDamping(float value) { _damping = value; } void setDamping(float value) { _damping = value; }
float getRestitution() const;
float getFriction() const;
// lifetime related properties. // lifetime related properties.
float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity
void setLifetime(float value) { _lifetime = value; } /// set the lifetime in seconds for the entity void setLifetime(float value) { _lifetime = value; } /// set the lifetime in seconds for the entity
@ -298,8 +302,8 @@ public:
glm::mat4 getEntityToWorldMatrix() const; glm::mat4 getEntityToWorldMatrix() const;
glm::mat4 getWorldToEntityMatrix() const; glm::mat4 getWorldToEntityMatrix() const;
glm::vec3 worldToEntity(const glm::vec3 point) const; glm::vec3 worldToEntity(const glm::vec3& point) const;
glm::vec3 entityToWorld(const glm::vec3 point) const; glm::vec3 entityToWorld(const glm::vec3& point) const;
protected: protected:

View file

@ -94,7 +94,7 @@ void DynamicCharacterController::preStep(btCollisionWorld* collisionWorld) {
} }
} }
void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld,btScalar dt) { void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
btVector3 actualVelocity = _rigidBody->getLinearVelocity(); btVector3 actualVelocity = _rigidBody->getLinearVelocity();
btScalar actualSpeed = actualVelocity.length(); btScalar actualSpeed = actualVelocity.length();
@ -315,10 +315,10 @@ void DynamicCharacterController::updateShapeIfNecessary() {
float mass = 1.0f; float mass = 1.0f;
btVector3 inertia(1.0f, 1.0f, 1.0f); btVector3 inertia(1.0f, 1.0f, 1.0f);
_rigidBody = new btRigidBody(mass, NULL, _shape, inertia); _rigidBody = new btRigidBody(mass, NULL, _shape, inertia);
_rigidBody->setSleepingThresholds (0.0f, 0.0f); _rigidBody->setSleepingThresholds(0.0f, 0.0f);
_rigidBody->setAngularFactor (0.0f); _rigidBody->setAngularFactor(0.0f);
_rigidBody->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()),
glmToBullet(_avatarData->getPosition()))); glmToBullet(_avatarData->getPosition())));
if (_isHovering) { if (_isHovering) {
_rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f));
} else { } else {

View file

@ -51,7 +51,7 @@ EntityMotionState::~EntityMotionState() {
_entity = NULL; _entity = NULL;
} }
MotionType EntityMotionState::computeMotionType() const { MotionType EntityMotionState::computeObjectMotionType() const {
if (_entity->getCollisionsWillMove()) { if (_entity->getCollisionsWillMove()) {
return MOTION_TYPE_DYNAMIC; return MOTION_TYPE_DYNAMIC;
} }
@ -68,7 +68,7 @@ void EntityMotionState::stepKinematicSimulation(quint64 now) {
// which is different from physical kinematic motion (inside getWorldTransform()) // which is different from physical kinematic motion (inside getWorldTransform())
// which steps in physics simulation time. // which steps in physics simulation time.
_entity->simulate(now); _entity->simulate(now);
// TODO: we can't use ObjectMotionState::measureAcceleration() here because the entity // TODO: we can't use measureBodyAcceleration() here because the entity
// has no RigidBody and the timestep is a little bit out of sync with the physics simulation anyway. // has no RigidBody and the timestep is a little bit out of sync with the physics simulation anyway.
// Hence we must manually measure kinematic velocity and acceleration. // Hence we must manually measure kinematic velocity and acceleration.
} }
@ -101,7 +101,7 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
// This callback is invoked by the physics simulation at the end of each simulation step... // This callback is invoked by the physics simulation at the end of each simulation step...
// iff the corresponding RigidBody is DYNAMIC and has moved. // iff the corresponding RigidBody is DYNAMIC and has moved.
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
measureAcceleration(); measureBodyAcceleration();
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
_entity->setRotation(bulletToGLM(worldTrans.getRotation())); _entity->setRotation(bulletToGLM(worldTrans.getRotation()));
@ -138,20 +138,20 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
#endif #endif
} }
void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) { void EntityMotionState::updateBodyEasy(uint32_t flags, uint32_t step) {
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY | EntityItem::DIRTY_PHYSICS_NO_WAKE)) { if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY | EntityItem::DIRTY_PHYSICS_NO_WAKE)) {
if (flags & EntityItem::DIRTY_POSITION) { if (flags & EntityItem::DIRTY_POSITION) {
_sentPosition = _entity->getPosition() - ObjectMotionState::getWorldOffset(); _sentPosition = getObjectPosition() - ObjectMotionState::getWorldOffset();
btTransform worldTrans; btTransform worldTrans;
worldTrans.setOrigin(glmToBullet(_sentPosition)); worldTrans.setOrigin(glmToBullet(_sentPosition));
_sentRotation = _entity->getRotation(); _sentRotation = getObjectRotation();
worldTrans.setRotation(glmToBullet(_sentRotation)); worldTrans.setRotation(glmToBullet(_sentRotation));
_body->setWorldTransform(worldTrans); _body->setWorldTransform(worldTrans);
} }
if (flags & EntityItem::DIRTY_VELOCITY) { if (flags & EntityItem::DIRTY_VELOCITY) {
updateObjectVelocities(); updateBodyVelocities();
} }
_sentStep = step; _sentStep = step;
@ -160,54 +160,54 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) {
} }
} }
// TODO: entity support for friction and restitution if (flags & EntityItem::DIRTY_MATERIAL) {
//_restitution = _entity->getRestitution(); updateBodyMaterialProperties();
_body->setRestitution(_restitution); }
//_friction = _entity->getFriction();
_body->setFriction(_friction);
_linearDamping = _entity->getDamping();
_angularDamping = _entity->getAngularDamping();
_body->setDamping(_linearDamping, _angularDamping);
if (flags & EntityItem::DIRTY_MASS) { if (flags & EntityItem::DIRTY_MASS) {
float mass = _entity->computeMass(); ShapeInfo shapeInfo;
_entity->computeShapeInfo(shapeInfo);
float mass = computeObjectMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
_body->getCollisionShape()->calculateLocalInertia(mass, inertia); _body->getCollisionShape()->calculateLocalInertia(mass, inertia);
_body->setMassProps(mass, inertia); _body->setMassProps(mass, inertia);
_body->updateInertiaTensor(); _body->updateInertiaTensor();
} }
}; }
void EntityMotionState::updateObjectVelocities() { void EntityMotionState::updateBodyMaterialProperties() {
_body->setRestitution(getObjectRestitution());
_body->setFriction(getObjectFriction());
_body->setDamping(fabsf(btMin(getObjectLinearDamping(), 1.0f)), fabsf(btMin(getObjectAngularDamping(), 1.0f)));
}
void EntityMotionState::updateBodyVelocities() {
if (_body) { if (_body) {
_sentVelocity = _entity->getVelocity(); _sentVelocity = getObjectLinearVelocity();
setVelocity(_sentVelocity); setBodyVelocity(_sentVelocity);
_sentAngularVelocity = _entity->getAngularVelocity(); _sentAngularVelocity = getObjectAngularVelocity();
setAngularVelocity(_sentAngularVelocity); setBodyAngularVelocity(_sentAngularVelocity);
_sentGravity = _entity->getGravity(); _sentGravity = getObjectGravity();
setGravity(_sentGravity); setBodyGravity(_sentGravity);
_body->setActivationState(ACTIVE_TAG); _body->setActivationState(ACTIVE_TAG);
} }
} }
void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) { void EntityMotionState::computeObjectShapeInfo(ShapeInfo& shapeInfo) {
if (_entity->isReadyToComputeShape()) { if (_entity->isReadyToComputeShape()) {
_entity->computeShapeInfo(shapeInfo); _entity->computeShapeInfo(shapeInfo);
} }
} }
float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const { float EntityMotionState::computeObjectMass(const ShapeInfo& shapeInfo) const {
return _entity->computeMass(); return _entity->computeMass();
} }
bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) { bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) {
bool baseResult = this->ObjectMotionState::shouldSendUpdate(simulationFrame); if (!ObjectMotionState::shouldSendUpdate(simulationFrame)) {
if (!baseResult) {
return false; return false;
} }

View file

@ -31,12 +31,11 @@ public:
static void setOutgoingEntityList(QSet<EntityItem*>* list); static void setOutgoingEntityList(QSet<EntityItem*>* list);
static void enqueueOutgoingEntity(EntityItem* entity); static void enqueueOutgoingEntity(EntityItem* entity);
EntityMotionState() = delete; // prevent compiler from making default ctor
EntityMotionState(EntityItem* item); EntityMotionState(EntityItem* item);
virtual ~EntityMotionState(); virtual ~EntityMotionState();
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
virtual MotionType computeMotionType() const; virtual MotionType computeObjectMotionType() const;
virtual void updateKinematicState(uint32_t substep); virtual void updateKinematicState(uint32_t substep);
virtual void stepKinematicSimulation(quint64 now); virtual void stepKinematicSimulation(quint64 now);
@ -50,11 +49,12 @@ public:
virtual void setWorldTransform(const btTransform& worldTrans); virtual void setWorldTransform(const btTransform& worldTrans);
// these relay incoming values to the RigidBody // these relay incoming values to the RigidBody
virtual void updateObjectEasy(uint32_t flags, uint32_t step); virtual void updateBodyEasy(uint32_t flags, uint32_t step);
virtual void updateObjectVelocities(); virtual void updateBodyMaterialProperties();
virtual void updateBodyVelocities();
virtual void computeShapeInfo(ShapeInfo& shapeInfo); virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo);
virtual float computeMass(const ShapeInfo& shapeInfo) const; virtual float computeObjectMass(const ShapeInfo& shapeInfo) const;
virtual bool shouldSendUpdate(uint32_t simulationFrame); virtual bool shouldSendUpdate(uint32_t simulationFrame);
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step); virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
@ -70,6 +70,17 @@ public:
virtual void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; } virtual void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; }
virtual bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; } virtual bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; }
virtual float getObjectRestitution() const { return _entity->getRestitution(); }
virtual float getObjectFriction() const { return _entity->getFriction(); }
virtual float getObjectLinearDamping() const { return _entity->getDamping(); }
virtual float getObjectAngularDamping() const { return _entity->getAngularDamping(); }
virtual const glm::vec3& getObjectPosition() const { return _entity->getPosition(); }
virtual const glm::quat& getObjectRotation() const { return _entity->getRotation(); }
virtual const glm::vec3& getObjectLinearVelocity() const { return _entity->getVelocity(); }
virtual const glm::vec3& getObjectAngularVelocity() const { return _entity->getAngularVelocity(); }
virtual const glm::vec3& getObjectGravity() const { return _entity->getGravity(); }
protected: protected:
EntityItem* _entity; EntityItem* _entity;
quint8 _accelerationNearlyGravityCount; quint8 _accelerationNearlyGravityCount;

View file

@ -36,17 +36,13 @@ const glm::vec3& ObjectMotionState::getWorldOffset() {
} }
// static // static
uint32_t _simulationStep = 0; uint32_t _worldSimulationStep = 0;
void ObjectMotionState::setSimulationStep(uint32_t step) { void ObjectMotionState::setWorldSimulationStep(uint32_t step) {
assert(step > _simulationStep); assert(step > _worldSimulationStep);
_simulationStep = step; _worldSimulationStep = step;
} }
ObjectMotionState::ObjectMotionState() : ObjectMotionState::ObjectMotionState() :
_friction(DEFAULT_FRICTION),
_restitution(DEFAULT_RESTITUTION),
_linearDamping(0.0f),
_angularDamping(0.0f),
_motionType(MOTION_TYPE_STATIC), _motionType(MOTION_TYPE_STATIC),
_body(NULL), _body(NULL),
_sentMoving(false), _sentMoving(false),
@ -69,52 +65,36 @@ ObjectMotionState::~ObjectMotionState() {
assert(_body == NULL); assert(_body == NULL);
} }
void ObjectMotionState::measureAcceleration() { void ObjectMotionState::measureBodyAcceleration() {
// try to manually measure the true acceleration of the object // try to manually measure the true acceleration of the object
uint32_t numSubsteps = _simulationStep - _lastSimulationStep; uint32_t numSubsteps = _worldSimulationStep - _lastSimulationStep;
if (numSubsteps > 0) { if (numSubsteps > 0) {
float dt = ((float)numSubsteps * PHYSICS_ENGINE_FIXED_SUBSTEP); float dt = ((float)numSubsteps * PHYSICS_ENGINE_FIXED_SUBSTEP);
float invDt = 1.0f / dt; float invDt = 1.0f / dt;
_lastSimulationStep = _simulationStep; _lastSimulationStep = _worldSimulationStep;
// Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt
// hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt
glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity());
_measuredAcceleration = (velocity / powf(1.0f - _linearDamping, dt) - _lastVelocity) * invDt; _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
_lastVelocity = velocity; _lastVelocity = velocity;
} }
} }
void ObjectMotionState::resetMeasuredAcceleration() { void ObjectMotionState::resetMeasuredBodyAcceleration() {
_lastSimulationStep = _simulationStep; _lastSimulationStep = _worldSimulationStep;
_lastVelocity = bulletToGLM(_body->getLinearVelocity()); _lastVelocity = bulletToGLM(_body->getLinearVelocity());
} }
void ObjectMotionState::setFriction(float friction) { void ObjectMotionState::setBodyVelocity(const glm::vec3& velocity) const {
_friction = btMax(btMin(fabsf(friction), MAX_FRICTION), 0.0f);
}
void ObjectMotionState::setRestitution(float restitution) {
_restitution = btMax(btMin(fabsf(restitution), 1.0f), 0.0f);
}
void ObjectMotionState::setLinearDamping(float damping) {
_linearDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f);
}
void ObjectMotionState::setAngularDamping(float damping) {
_angularDamping = btMax(btMin(fabsf(damping), 1.0f), 0.0f);
}
void ObjectMotionState::setVelocity(const glm::vec3& velocity) const {
_body->setLinearVelocity(glmToBullet(velocity)); _body->setLinearVelocity(glmToBullet(velocity));
} }
void ObjectMotionState::setAngularVelocity(const glm::vec3& velocity) const { void ObjectMotionState::setBodyAngularVelocity(const glm::vec3& velocity) const {
_body->setAngularVelocity(glmToBullet(velocity)); _body->setAngularVelocity(glmToBullet(velocity));
} }
void ObjectMotionState::setGravity(const glm::vec3& gravity) const { void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const {
_body->setGravity(glmToBullet(gravity)); _body->setGravity(glmToBullet(gravity));
} }
@ -181,7 +161,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationStep) {
// compute position error // compute position error
if (glm::length2(_sentVelocity) > 0.0f) { if (glm::length2(_sentVelocity) > 0.0f) {
_sentVelocity += _sentAcceleration * dt; _sentVelocity += _sentAcceleration * dt;
_sentVelocity *= powf(1.0f - _linearDamping, dt); _sentVelocity *= powf(1.0f - _body->getLinearDamping(), dt);
_sentPosition += dt * _sentVelocity; _sentPosition += dt * _sentVelocity;
} }
@ -206,7 +186,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationStep) {
if (glm::length2(_sentAngularVelocity) > 0.0f) { if (glm::length2(_sentAngularVelocity) > 0.0f) {
// compute rotation error // compute rotation error
float attenuation = powf(1.0f - _angularDamping, dt); float attenuation = powf(1.0f - _body->getAngularDamping(), dt);
_sentAngularVelocity *= attenuation; _sentAngularVelocity *= attenuation;
// Bullet caps the effective rotation velocity inside its rotation integration step, therefore // Bullet caps the effective rotation velocity inside its rotation integration step, therefore

View file

@ -36,7 +36,7 @@ enum MotionStateType {
// and re-added to the physics engine and "easy" which just updates the body properties. // and re-added to the physics engine and "easy" which just updates the body properties.
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE); const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY | const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY |
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP); EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP | EntityItem::DIRTY_MATERIAL);
// These are the set of incoming flags that the PhysicsEngine needs to hear about: // These are the set of incoming flags that the PhysicsEngine needs to hear about:
const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS; const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS;
@ -59,32 +59,30 @@ public:
static void setWorldOffset(const glm::vec3& offset); static void setWorldOffset(const glm::vec3& offset);
static const glm::vec3& getWorldOffset(); static const glm::vec3& getWorldOffset();
static void setSimulationStep(uint32_t step); // The WorldSimulationStep is a cached copy of number of SubSteps of the simulation, used for local time measurements.
static void setWorldSimulationStep(uint32_t step);
ObjectMotionState(); ObjectMotionState();
~ObjectMotionState(); ~ObjectMotionState();
void measureAcceleration(); void measureBodyAcceleration();
void resetMeasuredAcceleration(); void resetMeasuredBodyAcceleration();
// An EASY update does not require the object to be removed and then reinserted into the PhysicsEngine // An EASY update does not require the object to be removed and then reinserted into the PhysicsEngine
virtual void updateObjectEasy(uint32_t flags, uint32_t frame) = 0; virtual void updateBodyEasy(uint32_t flags, uint32_t frame) = 0;
virtual void updateObjectVelocities() = 0;
virtual void updateBodyMaterialProperties() = 0;
virtual void updateBodyVelocities() = 0;
MotionStateType getType() const { return _type; } MotionStateType getType() const { return _type; }
virtual MotionType getMotionType() const { return _motionType; } virtual MotionType getMotionType() const { return _motionType; }
virtual void computeShapeInfo(ShapeInfo& info) = 0; virtual void computeObjectShapeInfo(ShapeInfo& info) = 0;
virtual float computeMass(const ShapeInfo& shapeInfo) const = 0; virtual float computeObjectMass(const ShapeInfo& shapeInfo) const = 0;
void setFriction(float friction); void setBodyVelocity(const glm::vec3& velocity) const;
void setRestitution(float restitution); void setBodyAngularVelocity(const glm::vec3& velocity) const;
void setLinearDamping(float damping); void setBodyGravity(const glm::vec3& gravity) const;
void setAngularDamping(float damping);
void setVelocity(const glm::vec3& velocity) const;
void setAngularVelocity(const glm::vec3& velocity) const;
void setGravity(const glm::vec3& gravity) const;
void getVelocity(glm::vec3& velocityOut) const; void getVelocity(glm::vec3& velocityOut) const;
void getAngularVelocity(glm::vec3& angularVelocityOut) const; void getAngularVelocity(glm::vec3& angularVelocityOut) const;
@ -97,7 +95,7 @@ public:
virtual bool shouldSendUpdate(uint32_t simulationStep); virtual bool shouldSendUpdate(uint32_t simulationStep);
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) = 0; virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) = 0;
virtual MotionType computeMotionType() const = 0; virtual MotionType computeObjectMotionType() const = 0;
virtual void updateKinematicState(uint32_t substep) = 0; virtual void updateKinematicState(uint32_t substep) = 0;
@ -117,18 +115,26 @@ public:
virtual void setShouldClaimSimulationOwnership(bool value) { } virtual void setShouldClaimSimulationOwnership(bool value) { }
virtual bool getShouldClaimSimulationOwnership() { return false; } virtual bool getShouldClaimSimulationOwnership() { return false; }
// These pure virtual methods must be implemented for each MotionState type
// and make it possible to implement more complicated methods in this base class.
virtual float getObjectRestitution() const = 0;
virtual float getObjectFriction() const = 0;
virtual float getObjectLinearDamping() const = 0;
virtual float getObjectAngularDamping() const = 0;
virtual const glm::vec3& getObjectPosition() const = 0;
virtual const glm::quat& getObjectRotation() const = 0;
virtual const glm::vec3& getObjectLinearVelocity() const = 0;
virtual const glm::vec3& getObjectAngularVelocity() const = 0;
virtual const glm::vec3& getObjectGravity() const = 0;
protected: protected:
void setRigidBody(btRigidBody* body); void setRigidBody(btRigidBody* body);
MotionStateType _type = MOTION_STATE_TYPE_UNKNOWN; MotionStateType _type = MOTION_STATE_TYPE_UNKNOWN; // type of MotionState
// TODO: move these materials properties outside of ObjectMotionState MotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC
float _friction;
float _restitution;
float _linearDamping;
float _angularDamping;
MotionType _motionType;
btRigidBody* _body; btRigidBody* _body;

View file

@ -15,6 +15,7 @@
#include "ShapeInfoUtil.h" #include "ShapeInfoUtil.h"
#include "PhysicsHelpers.h" #include "PhysicsHelpers.h"
#include "ThreadSafeDynamicsWorld.h" #include "ThreadSafeDynamicsWorld.h"
#include "PhysicsLogging.h"
static uint32_t _numSubsteps; static uint32_t _numSubsteps;
@ -172,9 +173,9 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
if (flags & HARD_DIRTY_PHYSICS_FLAGS) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
// a HARD update requires the body be pulled out of physics engine, changed, then reinserted // a HARD update requires the body be pulled out of physics engine, changed, then reinserted
// but it also handles all EASY changes // but it also handles all EASY changes
bool success = updateObjectHard(body, motionState, flags); bool success = updateBodyHard(body, motionState, flags);
if (!success) { if (!success) {
// NOTE: since updateObjectHard() failed we know that motionState has been removed // NOTE: since updateBodyHard() failed we know that motionState has been removed
// from simulation and body has been deleted. Depending on what else has changed // from simulation and body has been deleted. Depending on what else has changed
// we might need to remove motionState altogether... // we might need to remove motionState altogether...
if (flags & EntityItem::DIRTY_VELOCITY) { if (flags & EntityItem::DIRTY_VELOCITY) {
@ -194,10 +195,10 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
} else if (flags) { } else if (flags) {
// an EASY update does NOT require that the body be pulled out of physics engine // an EASY update does NOT require that the body be pulled out of physics engine
// hence the MotionState has all the knowledge and authority to perform the update. // hence the MotionState has all the knowledge and authority to perform the update.
motionState->updateObjectEasy(flags, _numSubsteps); motionState->updateBodyEasy(flags, _numSubsteps);
} }
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) { if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
motionState->resetMeasuredAcceleration(); motionState->resetMeasuredBodyAcceleration();
} }
} else { } else {
// the only way we should ever get here (motionState exists but no body) is when the object // the only way we should ever get here (motionState exists but no body) is when the object
@ -207,7 +208,7 @@ void PhysicsEngine::relayIncomingChangesToSimulation() {
// it is possible that the changes are such that the object can now be added to the physical simulation // it is possible that the changes are such that the object can now be added to the physical simulation
if (flags & EntityItem::DIRTY_SHAPE) { if (flags & EntityItem::DIRTY_SHAPE) {
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo); motionState->computeObjectShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo); btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) { if (shape) {
addObject(shapeInfo, shape, motionState); addObject(shapeInfo, shape, motionState);
@ -341,7 +342,7 @@ void PhysicsEngine::stepSimulation() {
// lock on the tree before we re-lock ourselves. // lock on the tree before we re-lock ourselves.
// //
// TODO: untangle these lock sequences. // TODO: untangle these lock sequences.
ObjectMotionState::setSimulationStep(_numSubsteps); ObjectMotionState::setWorldSimulationStep(_numSubsteps);
_entityTree->lockForWrite(); _entityTree->lockForWrite();
lock(); lock();
_dynamicsWorld->synchronizeMotionStates(); _dynamicsWorld->synchronizeMotionStates();
@ -498,7 +499,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f; float mass = 0.0f;
btRigidBody* body = NULL; btRigidBody* body = NULL;
switch(motionState->computeMotionType()) { switch(motionState->computeObjectMotionType()) {
case MOTION_TYPE_KINEMATIC: { case MOTION_TYPE_KINEMATIC: {
body = new btRigidBody(mass, motionState, shape, inertia); body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
@ -511,13 +512,13 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
break; break;
} }
case MOTION_TYPE_DYNAMIC: { case MOTION_TYPE_DYNAMIC: {
mass = motionState->computeMass(shapeInfo); mass = motionState->computeObjectMass(shapeInfo);
shape->calculateLocalInertia(mass, inertia); shape->calculateLocalInertia(mass, inertia);
body = new btRigidBody(mass, motionState, shape, inertia); body = new btRigidBody(mass, motionState, shape, inertia);
body->updateInertiaTensor(); body->updateInertiaTensor();
motionState->setRigidBody(body); motionState->setRigidBody(body);
motionState->setKinematic(false, _numSubsteps); motionState->setKinematic(false, _numSubsteps);
motionState->updateObjectVelocities(); motionState->updateBodyVelocities();
// NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds. // NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds.
// (the 2 seconds is determined by: static btRigidBody::gDeactivationTime // (the 2 seconds is determined by: static btRigidBody::gDeactivationTime
const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
@ -540,11 +541,10 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
} }
} }
body->setFlags(BT_DISABLE_WORLD_GRAVITY); body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution); motionState->updateBodyMaterialProperties();
body->setFriction(motionState->_friction);
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
_dynamicsWorld->addRigidBody(body); _dynamicsWorld->addRigidBody(body);
motionState->resetMeasuredAcceleration(); motionState->resetMeasuredBodyAcceleration();
} }
void PhysicsEngine::bump(EntityItem* bumpEntity) { void PhysicsEngine::bump(EntityItem* bumpEntity) {
@ -608,8 +608,8 @@ void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) {
} }
// private // private
bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { bool PhysicsEngine::updateBodyHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
MotionType newType = motionState->computeMotionType(); MotionType newType = motionState->computeObjectMotionType();
// pull body out of physics engine // pull body out of physics engine
_dynamicsWorld->removeRigidBody(body); _dynamicsWorld->removeRigidBody(body);
@ -621,7 +621,7 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
// get new shape // get new shape
btCollisionShape* oldShape = body->getCollisionShape(); btCollisionShape* oldShape = body->getCollisionShape();
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo); motionState->computeObjectShapeInfo(shapeInfo);
btCollisionShape* newShape = _shapeManager.getShape(shapeInfo); btCollisionShape* newShape = _shapeManager.getShape(shapeInfo);
if (!newShape) { if (!newShape) {
// FAIL! we are unable to support these changes! // FAIL! we are unable to support these changes!
@ -640,7 +640,7 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
_shapeManager.releaseShape(oldShape); _shapeManager.releaseShape(oldShape);
// compute mass properties // compute mass properties
float mass = motionState->computeMass(shapeInfo); float mass = motionState->computeObjectMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
body->getCollisionShape()->calculateLocalInertia(mass, inertia); body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia); body->setMassProps(mass, inertia);
@ -653,7 +653,7 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
} }
bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS; bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS;
if (easyUpdate) { if (easyUpdate) {
motionState->updateObjectEasy(flags, _numSubsteps); motionState->updateBodyEasy(flags, _numSubsteps);
} }
// update the motion parameters // update the motion parameters
@ -675,8 +675,8 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
if (! (flags & EntityItem::DIRTY_MASS)) { if (! (flags & EntityItem::DIRTY_MASS)) {
// always update mass properties when going dynamic (unless it's already been done above) // always update mass properties when going dynamic (unless it's already been done above)
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo); motionState->computeObjectShapeInfo(shapeInfo);
float mass = motionState->computeMass(shapeInfo); float mass = motionState->computeObjectMass(shapeInfo);
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
body->getCollisionShape()->calculateLocalInertia(mass, inertia); body->getCollisionShape()->calculateLocalInertia(mass, inertia);
body->setMassProps(mass, inertia); body->setMassProps(mass, inertia);

View file

@ -39,8 +39,8 @@ public:
ContactKey(void* a, void* b) : _a(a), _b(b) {} ContactKey(void* a, void* b) : _a(a), _b(b) {}
bool operator<(const ContactKey& other) const { return _a < other._a || (_a == other._a && _b < other._b); } bool operator<(const ContactKey& other) const { return _a < other._a || (_a == other._a && _b < other._b); }
bool operator==(const ContactKey& other) const { return _a == other._a && _b == other._b; } bool operator==(const ContactKey& other) const { return _a == other._a && _b == other._b; }
void* _a; void* _a; // EntityMotionState pointer
void* _b; void* _b; // EntityMotionState pointer
}; };
typedef std::map<ContactKey, ContactInfo> ContactMap; typedef std::map<ContactKey, ContactInfo> ContactMap;
@ -100,7 +100,7 @@ private:
void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB); void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB);
// return 'true' of update was successful // return 'true' of update was successful
bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); bool updateBodyHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
btClock _clock; btClock _clock;

View file

@ -426,12 +426,12 @@ bool Model::updateGeometry() {
_dilatedTextures.clear(); _dilatedTextures.clear();
_geometry = geometry; _geometry = geometry;
_meshGroupsKnown = false; _meshGroupsKnown = false;
setJointStates(newJointStates); initJointStates(newJointStates);
needToRebuild = true; needToRebuild = true;
} else if (_jointStates.isEmpty()) { } else if (_jointStates.isEmpty()) {
const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();
if (fbxGeometry.joints.size() > 0) { if (fbxGeometry.joints.size() > 0) {
setJointStates(createJointStates(fbxGeometry)); initJointStates(createJointStates(fbxGeometry));
needToRebuild = true; needToRebuild = true;
} }
} else if (!geometry->isLoaded()) { } else if (!geometry->isLoaded()) {
@ -469,7 +469,7 @@ bool Model::updateGeometry() {
} }
// virtual // virtual
void Model::setJointStates(QVector<JointState> states) { void Model::initJointStates(QVector<JointState> states) {
_jointStates = states; _jointStates = states;
initJointTransforms(); initJointTransforms();

View file

@ -244,7 +244,7 @@ protected:
// returns 'true' if needs fullUpdate after geometry change // returns 'true' if needs fullUpdate after geometry change
bool updateGeometry(); bool updateGeometry();
virtual void setJointStates(QVector<JointState> states); virtual void initJointStates(QVector<JointState> states);
void setScaleInternal(const glm::vec3& scale); void setScaleInternal(const glm::vec3& scale);
void scaleToFit(); void scaleToFit();