Skeleton now creates VerletShape

This commit is contained in:
Andrew Meadows 2014-06-18 17:22:39 -07:00
parent 715cc3467d
commit d4b5550cda
10 changed files with 117 additions and 189 deletions

View file

@ -218,9 +218,6 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderSkeleton || renderHead || renderBounding) {
updateShapePositions();
}
if (renderSkeleton) {
_skeletonModel.renderJointCollisionShapes(0.7f);
@ -590,18 +587,6 @@ bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisio
getHead()->getFaceModel().findPlaneCollisions(plane, collisions);
}
void Avatar::updateShapePositions() {
_skeletonModel.updateShapePositions();
Model& headModel = getHead()->getFaceModel();
headModel.updateShapePositions();
/* KEEP FOR DEBUG: use this in rather than code above to see shapes
* in their default positions where the bounding shape is computed.
_skeletonModel.resetShapePositions();
Model& headModel = getHead()->getFaceModel();
headModel.resetShapePositions();
*/
}
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
// TODO: Andrew to fix: also collide against _skeleton
//bool collided = _skeletonModel.findCollisions(shapes, collisions);

View file

@ -136,7 +136,6 @@ public:
/// \return bounding radius of avatar
virtual float getBoundingRadius() const;
void updateShapePositions();
quint32 getCollisionGroups() const { return _collisionGroups; }
virtual void setCollisionGroups(quint32 collisionGroups) { _collisionGroups = (collisionGroups & VALID_COLLISION_GROUPS); }

View file

@ -210,7 +210,6 @@ void MyAvatar::simulate(float deltaTime) {
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
radius *= COLLISION_RADIUS_SCALAR;
}
updateShapePositions();
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithEnvironment");
updateCollisionWithEnvironment(deltaTime, radius);
@ -1447,7 +1446,6 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
// don't collide with ourselves
continue;
}
avatar->updateShapePositions();
float distance = glm::length(_position - avatar->getPosition());
if (_distanceToNearestAvatar > distance) {
_distanceToNearestAvatar = distance;

View file

@ -11,8 +11,8 @@
#include <glm/gtx/transform.hpp>
#include <CapsuleShape.h>
#include <SphereShape.h>
#include <VerletCapsuleShape.h>
#include <VerletSphereShape.h>
#include "Application.h"
#include "Avatar.h"
@ -31,20 +31,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
void SkeletonModel::setJointStates(QVector<JointState> states) {
Model::setJointStates(states);
if (isActive() && _owningAvatar->isMyAvatar()) {
// extract lists of parentIndex and position from _jointStates...
QVector<int> parentIndices;
QVector<glm::vec3> points;
int numJoints = _jointStates.size();
parentIndices.reserve(numJoints);
points.reserve(numJoints);
for (int i = 0; i < numJoints; ++i) {
JointState& state = _jointStates[i];
parentIndices.push_back(state.getFBXJoint().parentIndex);
points.push_back(state.getPosition());
}
// ... and feed the results to Ragdoll
initShapesAndPoints(&_shapes, parentIndices, points);
clearShapes();
clearRagdollConstraintsAndPoints();
if (_enableShapes) {
buildShapes();
}
}
@ -110,14 +100,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
}
void SkeletonModel::simulateRagdoll(float deltaTime) {
// move ragdoll _points toward joints
// move _ragdollPoints toward joints
const int numStates = _jointStates.size();
assert(numStates == _points.size());
assert(numStates == _ragdollPoints.size());
float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes
float oneMinusFraction = 1.0f - fraction;
for (int i = 0; i < numStates; ++i) {
_points[i] = oneMinusFraction * _points[i] + fraction * _jointStates[i].getPosition();
}
moveShapesTowardJoints(fraction);
// enforce the constraints
float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm
@ -125,7 +112,7 @@ void SkeletonModel::simulateRagdoll(float deltaTime) {
int iterations = 0;
float delta = 0.0f;
do {
delta = enforceConstraints();
delta = enforceRagdollConstraints();
++iterations;
} while (delta > MIN_CONSTRAINT_ERROR && iterations < MAX_ITERATIONS);
}
@ -274,16 +261,6 @@ void SkeletonModel::updateJointState(int index) {
}
}
void SkeletonModel::updateShapePositions() {
if (isActive() && _owningAvatar->isMyAvatar() &&
Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) {
// TODO: Andrew to move shape updates to SimulationEngine
//updateShapes(_rotation, _translation);
} else {
Model::updateShapePositions();
}
}
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) {
return;
@ -516,14 +493,14 @@ void SkeletonModel::renderRagdoll() {
glPushMatrix();
Application::getInstance()->loadTranslatedViewMatrix(_translation);
int numPoints = _points.size();
int numPoints = _ragdollPoints.size();
float alpha = 0.3f;
float radius1 = 0.008f;
float radius2 = 0.01f;
for (int i = 0; i < numPoints; ++i) {
glPushMatrix();
// draw each point as a yellow hexagon with black border
glm::vec3 position = _rotation * _points[i];
glm::vec3 position = _rotation * _ragdollPoints[i];
glTranslatef(position.x, position.y, position.z);
glColor4f(0.0f, 0.0f, 0.0f, alpha);
glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
@ -536,6 +513,22 @@ void SkeletonModel::renderRagdoll() {
glEnable(GL_LIGHTING);
}
// virtual
void SkeletonModel::initRagdollPoints() {
assert(_ragdollPoints.size() == 0);
// one point for each joint
int numJoints = _jointStates.size();
_ragdollPoints.reserve(numJoints);
for (int i = 0; i < numJoints; ++i) {
const JointState& state = _jointStates.at(i);
_ragdollPoints.push_back(state.getPosition());
}
}
// virtual
void SkeletonModel::stepRagdollForward(float deltaTime) {
}
// virtual
void SkeletonModel::buildShapes() {
if (!_geometry || _rootIndex == -1) {
@ -546,42 +539,53 @@ void SkeletonModel::buildShapes() {
if (geometry.joints.isEmpty()) {
return;
}
// We create the shapes with proper dimensions, but we set their transforms later.
float uniformScale = extractUniformScale(_scale);
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
initRagdollPoints();
float uniformScale = extractUniformScale(_scale);
const int numStates = _jointStates.size();
for (int i = 0; i < numStates; i++) {
const FBXJoint& joint = geometry.joints[i];
float radius = uniformScale * joint.boneRadius;
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
Shape::Type type = joint.shapeType;
if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) {
// this capsule is effectively a sphere
if (i == 0 || (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON)) {
// this shape is forced to be a sphere
type = Shape::SPHERE_SHAPE;
}
if (type == Shape::CAPSULE_SHAPE) {
CapsuleShape* capsule = new CapsuleShape(radius, halfHeight);
capsule->setEntity(this);
_shapes.push_back(capsule);
} else if (type == Shape::SPHERE_SHAPE) {
SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f));
_shapes.push_back(sphere);
sphere->setEntity(this);
} else {
// this shape type is not handled and the joint shouldn't collide,
// however we must have a Shape* for each joint, so we push NULL
_shapes.push_back(NULL);
}
Shape* shape = NULL;
if (type == Shape::SPHERE_SHAPE) {
shape = new VerletSphereShape(radius, &(_ragdollPoints[i]));
shape->setEntity(this);
} else if (type == Shape::CAPSULE_SHAPE) {
int parentIndex = joint.parentIndex;
assert(parentIndex != -1);
shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i]));
shape->setEntity(this);
}
_shapes.push_back(shape);
}
// This method moves the shapes to their default positions in Model frame
// which is where we compute the bounding shape's parameters.
computeBoundingShape(geometry);
// finally sync shapes to joint positions
_shapesAreDirty = true;
updateShapePositions();
// move shapes to joint positions
moveShapesTowardJoints(1.0f);
}
void SkeletonModel::moveShapesTowardJoints(float fraction) {
assert(fraction >= 0.0f && fraction <= 1.0f);
if (_ragdollPoints.size() == _jointStates.size()) {
float oneMinusFraction = 1.0f - fraction;
int numJoints = _jointStates.size();
for (int i = 0; i < numJoints; ++i) {
_ragdollPoints[i] = oneMinusFraction * _ragdollPoints[i] + fraction * _jointStates.at(i).getPosition();
}
}
}
/* TODO: Andrew to remove this when done with ragdoll work.
void SkeletonModel::updateShapePositionsLegacy() {
if (_shapesAreDirty && _shapes.size() == _jointStates.size()) {
glm::vec3 rootPosition(0.0f);
@ -612,65 +616,28 @@ void SkeletonModel::updateShapePositionsLegacy() {
_boundingShape.setRotation(_rotation);
}
}
*/
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
// compute default joint transforms and rotations
// (in local frame, ignoring Model translation and rotation)
// compute default joint transforms
int numJoints = geometry.joints.size();
if (numJoints != _ragdollPoints.size()) {
return;
}
QVector<glm::mat4> transforms;
transforms.fill(glm::mat4(), numJoints);
QVector<glm::quat> finalRotations;
finalRotations.fill(glm::quat(), numJoints);
QVector<bool> shapeIsSet;
shapeIsSet.fill(false, numJoints);
int numShapesSet = 0;
int lastNumShapesSet = -1;
while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) {
lastNumShapesSet = numShapesSet;
for (int i = 0; i < numJoints; i++) {
const FBXJoint& joint = geometry.joints.at(i);
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
glm::mat4 baseTransform = glm::scale(_scale) * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
glm::mat4 rootTransform = baseTransform * geometry.offset * glm::translate(joint.translation)
* joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform;
// remove the tranlsation part before we save the root transform
transforms[i] = glm::translate(- extractTranslation(rootTransform)) * rootTransform;
finalRotations[i] = combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
} else if (shapeIsSet[parentIndex]) {
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
* joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform;
finalRotations[i] = finalRotations[parentIndex] * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
}
}
}
// sync shapes to joints
_boundingRadius = 0.0f;
float uniformScale = extractUniformScale(_scale);
for (int i = 0; i < _shapes.size(); i++) {
Shape* shape = _shapes[i];
if (!shape) {
continue;
}
const FBXJoint& joint = geometry.joints[i];
glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition);
glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset;
shape->setTranslation(localPosition);
shape->setRotation(finalRotations[i] * joint.shapeRotation);
float distance = glm::length(localPosition) + shape->getBoundingRadius();
if (distance > _boundingRadius) {
_boundingRadius = distance;
}
transforms[0] = glm::scale(_scale);
for (int i = 1; i < numJoints; i++) {
const FBXJoint& joint = geometry.joints.at(i);
int parentIndex = joint.parentIndex;
assert(parentIndex != -1);
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
* joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform;
// setting the ragdollPoints here slams the VerletShapes into their default positions
_ragdollPoints[i] = extractTranslation(transforms[i]);
}
// compute bounding box
@ -682,6 +649,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
if (!shape) {
continue;
}
// TODO: skip hand and arm joints when computing bounding dimensions
Extents shapeExtents;
shapeExtents.reset();
glm::vec3 localPosition = shape->getTranslation();
@ -715,6 +683,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
_boundingShape.setRadius(capsuleRadius);
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum);
_boundingRadius = 0.5f * glm::length(diagonal);
}
void SkeletonModel::resetShapePositions() {

View file

@ -95,9 +95,12 @@ public:
/// \return whether or not both eye meshes were found
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
virtual void initRagdollPoints();
virtual void stepRagdollForward(float deltaTime);
void buildShapes();
void updateShapePositions();
void updateShapePositionsLegacy();
void moveShapesTowardJoints(float fraction);
//void updateShapePositionsLegacy(); // TODO: Andrew to remove this when done with ragdoll work
void computeBoundingShape(const FBXGeometry& geometry);
void renderBoundingCollisionShapes(float alpha);

View file

@ -551,9 +551,6 @@ bool Model::updateGeometry() {
model->setURL(attachment.url);
_attachments.append(model);
}
if (_enableShapes) {
buildShapes();
}
needFullUpdate = true;
}
return needFullUpdate;

View file

@ -92,7 +92,7 @@ bool PhysicalEntity::findCollisions(const QVector<const Shape*> shapes, Collisio
}
int numOurShapes = _shapes.size();
for (int j = 0; j < numOurShapes; ++j) {
const Shape* ourShape = _shapes[j];
const Shape* ourShape = _shapes.at(j);
if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) {
collided = true;
}
@ -142,7 +142,7 @@ bool PhysicalEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList&
bool collided = false;
PlaneShape planeShape(plane);
for (int i = 0; i < _shapes.size(); i++) {
if (_shapes[i] && ShapeCollider::collideShapes(&planeShape, _shapes[i], collisions)) {
if (_shapes.at(i) && ShapeCollider::collideShapes(&planeShape, _shapes.at(i), collisions)) {
CollisionInfo* collision = collisions.getLastCollision();
collision->_data = (void*)(this);
collision->_intData = i;

View file

@ -94,66 +94,55 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio
// Ragdoll
// ----------------------------------------------------------------------------
Ragdoll::Ragdoll() : _verletShapes(NULL) {
Ragdoll::Ragdoll() {
}
Ragdoll::~Ragdoll() {
clear();
clearRagdollConstraintsAndPoints();
}
void Ragdoll::initShapesAndPoints(QVector<Shape*>* shapes, const QVector<int>& parentIndices, const QVector<glm::vec3>& points) {
/*
void Ragdoll::useShapesAndCopyPoints(QVector<Shape*>* shapes, const QVector<int>& parentIndices, const QVector<glm::vec3>& points) {
clear();
_verletShapes = shapes;
const int numPoints = points.size();
assert(numPoints == parentIndices.size());
_points.reserve(numPoints);
_ragdollPoints.reserve(numPoints);
for (int i = 0; i < numPoints; ++i) {
glm::vec3 position = points[i];
_points.push_back(position);
_ragdollPoints.push_back(position);
int parentIndex = parentIndices[i];
assert(parentIndex < i && parentIndex >= -1);
if (parentIndex == -1) {
FixedConstraint* anchor = new FixedConstraint(&(_points[i]), glm::vec3(0.0f));
_constraints.push_back(anchor);
FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]), glm::vec3(0.0f));
_ragdollConstraints.push_back(anchor);
} else {
DistanceConstraint* stick = new DistanceConstraint(&(_points[i]), &(_points[parentIndex]));
_constraints.push_back(stick);
DistanceConstraint* stick = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex]));
_ragdollConstraints.push_back(stick);
}
}
}
*/
/// Delete all data.
void Ragdoll::clear() {
int numConstraints = _constraints.size();
void Ragdoll::clearRagdollConstraintsAndPoints() {
int numConstraints = _ragdollConstraints.size();
for (int i = 0; i < numConstraints; ++i) {
delete _constraints[i];
delete _ragdollConstraints[i];
}
_constraints.clear();
_points.clear();
_verletShapes = NULL;
_ragdollConstraints.clear();
_ragdollPoints.clear();
}
float Ragdoll::enforceConstraints() {
float Ragdoll::enforceRagdollConstraints() {
float maxDistance = 0.0f;
const int numConstraints = _constraints.size();
const int numConstraints = _ragdollConstraints.size();
for (int i = 0; i < numConstraints; ++i) {
DistanceConstraint* c = static_cast<DistanceConstraint*>(_constraints[i]);
//maxDistance = glm::max(maxDistance, _constraints[i]->enforce());
DistanceConstraint* c = static_cast<DistanceConstraint*>(_ragdollConstraints[i]);
//maxDistance = glm::max(maxDistance, _ragdollConstraints[i]->enforce());
maxDistance = glm::max(maxDistance, c->enforce());
}
return maxDistance;
}
void Ragdoll::updateShapes(const glm::quat& rotation, const glm::vec3& translation) const {
if (!_verletShapes) {
return;
}
int numShapes = _verletShapes->size();
int numConstraints = _constraints.size();
// NOTE: we assume a one-to-one relationship between shapes and constraints
for (int i = 0; i < numShapes && i < numConstraints; ++i) {
_constraints[i]->updateProxyShape((*_verletShapes)[i], rotation, translation);
}
}

View file

@ -66,38 +66,24 @@ public:
Ragdoll();
virtual ~Ragdoll();
/// Create points and constraints based on topology of collection of joints
/// \param joints list of connected joint states
void initShapesAndPoints(QVector<Shape*>* shapes, const QVector<int>& parentIndices, const QVector<glm::vec3>& points);
/// Delete all data.
void clear();
void clearRagdollConstraintsAndPoints();
// TODO: Andrew to implement this
void stepForward(float deltaTime) {}
virtual void initRagdollPoints() = 0;
virtual void stepRagdollForward(float deltaTime) = 0;
/// Enforce contraints.
/// \return max distance of point movement
float enforceConstraints();
float enforceRagdollConstraints();
// both const and non-const getPoints()
const QVector<glm::vec3>& getPoints() const { return _points; }
QVector<glm::vec3>& getPoints() { return _points; }
/// \param rotation rotation into shapes' collision frame
/// \param translation translation into shapes' collision frame
/// Moves and modifies elements of _verletShapes to agree with state of _points
void updateShapes(const glm::quat& rotation, const glm::vec3& translation) const;
const QVector<Shape*>* getShapes() const { return _verletShapes; }
const QVector<glm::vec3>& getRagdollPoints() const { return _ragdollPoints; }
QVector<glm::vec3>& getRagdollPoints() { return _ragdollPoints; }
protected:
QVector<glm::vec3> _points;
QVector<Constraint*> _constraints;
// the Ragdoll does NOT own the data in _verletShapes.
QVector<Shape*>* _verletShapes;
QVector<glm::vec3> _ragdollPoints;
QVector<Constraint*> _ragdollConstraints;
};
#endif // hifi_Ragdoll_h

View file

@ -67,6 +67,7 @@ void SimulationEngine::removeRagdoll(Ragdoll* doll) {
}
void SimulationEngine::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) {
/* TODO: Andrew to make this work
int iterations = 0;
float delta = 0.0f;
quint64 now = usecTimestampNow();
@ -82,7 +83,7 @@ void SimulationEngine::stepForward(float deltaTime, float minError, int maxItera
// (4) collisions move points (SpecialCapsuleShape would help solve this)
// (5) enforce constraints
// (6) add and enforce angular contraints for joints
_dolls.at(i)->stepForward(deltaTime);
//_dolls.at(i)->stepForward(deltaTime);
}
@ -128,6 +129,7 @@ void SimulationEngine::stepForward(float deltaTime, float minError, int maxItera
_enforcementIterations = iterations;
_enforcementError = delta;
_enforcementTime = now - startTime;
*/
}
int SimulationEngine::computeCollisions() {