From ecbf5043d74eb9f35412a38f0f8cda2afc453c3b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 16:22:18 -0700 Subject: [PATCH] code out of Model into base and derived classes PhysicalEntity (base class) gets some shape management stuff SkeletonModel (derived class) gets some boundary shape and joint-shape stuff --- interface/src/avatar/Avatar.cpp | 1 - interface/src/avatar/SkeletonModel.cpp | 255 +++++++++++++++++- interface/src/avatar/SkeletonModel.h | 16 +- interface/src/renderer/Model.cpp | 339 +----------------------- interface/src/renderer/Model.h | 27 +- libraries/shared/src/PhysicalEntity.cpp | 93 ++++++- libraries/shared/src/PhysicalEntity.h | 17 +- 7 files changed, 371 insertions(+), 377 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7a45512778..f940a106d7 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -230,7 +230,6 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } if (renderBounding && shouldRenderHead(cameraPosition, renderMode)) { - getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f); _skeletonModel.renderBoundingCollisionShapes(0.7f); } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c98766f479..364044a803 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -11,6 +11,9 @@ #include +#include +#include + #include "Application.h" #include "Avatar.h" #include "Hand.h" @@ -20,7 +23,9 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : Model(parent), Ragdoll(), - _owningAvatar(owningAvatar) { + _owningAvatar(owningAvatar), + _boundingShape(), + _boundingShapeLocalOffset(0.0f) { } void SkeletonModel::setJointStates(QVector states) { @@ -524,3 +529,251 @@ void SkeletonModel::renderRagdoll() { glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); } + +// virtual +void SkeletonModel::buildShapes() { + if (!_geometry || _rootIndex == -1) { + return; + } + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + 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]; + + 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 + 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); + } + } + + // 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(); +} + +void SkeletonModel::updateShapePositionsLegacy() { + if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { + glm::vec3 rootPosition(0.0f); + _boundingRadius = 0.0f; + float uniformScale = extractUniformScale(_scale); + for (int i = 0; i < _jointStates.size(); i++) { + const JointState& state = _jointStates[i]; + const FBXJoint& joint = state.getFBXJoint(); + // shape position and rotation need to be in world-frame + glm::quat stateRotation = state.getRotation(); + glm::vec3 shapeOffset = uniformScale * (stateRotation * joint.shapePosition); + glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); + Shape* shape = _shapes[i]; + if (shape) { + shape->setCenter(worldPosition); + shape->setRotation(_rotation * stateRotation * joint.shapeRotation); + float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); + if (distance > _boundingRadius) { + _boundingRadius = distance; + } + } + if (joint.parentIndex == -1) { + rootPosition = worldPosition; + } + } + _shapesAreDirty = false; + _boundingShape.setCenter(rootPosition + _rotation * _boundingShapeLocalOffset); + _boundingShape.setRotation(_rotation); + } +} + +void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { + // compute default joint transforms and rotations + // (in local frame, ignoring Model translation and rotation) + int numJoints = geometry.joints.size(); + QVector transforms; + transforms.fill(glm::mat4(), numJoints); + QVector finalRotations; + finalRotations.fill(glm::quat(), numJoints); + + QVector 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->setCenter(localPosition); + shape->setRotation(finalRotations[i] * joint.shapeRotation); + float distance = glm::length(localPosition) + shape->getBoundingRadius(); + if (distance > _boundingRadius) { + _boundingRadius = distance; + } + } + + // compute bounding box + Extents totalExtents; + totalExtents.reset(); + totalExtents.addPoint(glm::vec3(0.0f)); + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; + if (!shape) { + continue; + } + Extents shapeExtents; + shapeExtents.reset(); + glm::vec3 localPosition = shape->getCenter(); + int type = shape->getType(); + if (type == Shape::CAPSULE_SHAPE) { + // add the two furthest surface points of the capsule + CapsuleShape* capsule = static_cast(shape); + glm::vec3 axis; + capsule->computeNormalizedAxis(axis); + float radius = capsule->getRadius(); + float halfHeight = capsule->getHalfHeight(); + axis = halfHeight * axis + glm::vec3(radius); + + shapeExtents.addPoint(localPosition + axis); + shapeExtents.addPoint(localPosition - axis); + totalExtents.addExtents(shapeExtents); + } else if (type == Shape::SPHERE_SHAPE) { + float radius = shape->getBoundingRadius(); + glm::vec3 axis = glm::vec3(radius); + shapeExtents.addPoint(localPosition + axis); + shapeExtents.addPoint(localPosition - axis); + totalExtents.addExtents(shapeExtents); + } + } + + // compute bounding shape parameters + // NOTE: we assume that the longest side of totalExtents is the yAxis... + glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; + // ... and assume the radius is half the RMS of the X and Z sides: + float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + _boundingShape.setRadius(capsuleRadius); + _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); + _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); +} + +void SkeletonModel::resetShapePositions() { + // DEBUG method. + // Moves shapes to the joint default locations for debug visibility into + // how the bounding shape is computed. + + if (!_geometry || _rootIndex == -1 || _shapes.isEmpty()) { + // geometry or joints have not yet been created + return; + } + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (geometry.joints.isEmpty() || _shapes.size() != geometry.joints.size()) { + return; + } + + // The shapes are moved to their default positions in computeBoundingShape(). + computeBoundingShape(geometry); + + // Then we move them into world frame for rendering at the Model's location. + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; + if (shape) { + shape->setCenter(_translation + _rotation * shape->getCenter()); + shape->setRotation(_rotation * shape->getRotation()); + } + } + _boundingShape.setCenter(_translation + _rotation * _boundingShapeLocalOffset); + _boundingShape.setRotation(_rotation); +} + +void SkeletonModel::renderBoundingCollisionShapes(float alpha) { + const int BALL_SUBDIVISIONS = 10; + if (_shapes.isEmpty()) { + // the bounding shape has not been propery computed + // so no need to render it + return; + } + glPushMatrix(); + + Application::getInstance()->loadTranslatedViewMatrix(_translation); + + // draw a blue sphere at the capsule endpoint + glm::vec3 endPoint; + _boundingShape.getEndPoint(endPoint); + endPoint = endPoint - _translation; + glTranslatef(endPoint.x, endPoint.y, endPoint.z); + glColor4f(0.6f, 0.6f, 0.8f, alpha); + glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); + + // draw a yellow sphere at the capsule startpoint + glm::vec3 startPoint; + _boundingShape.getStartPoint(startPoint); + startPoint = startPoint - _translation; + glm::vec3 axis = endPoint - startPoint; + glTranslatef(-axis.x, -axis.y, -axis.z); + glColor4f(0.8f, 0.8f, 0.6f, alpha); + glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); + + // draw a green cylinder between the two points + glm::vec3 origin(0.0f); + glColor4f(0.6f, 0.8f, 0.6f, alpha); + Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius()); + + glPopMatrix(); +} + diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index f0982e6dd7..8d37697aef 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -14,6 +14,7 @@ #include "renderer/Model.h" +#include #include class Avatar; @@ -30,7 +31,6 @@ public: void simulate(float deltaTime, bool fullUpdate = true); void simulateRagdoll(float deltaTime); - void updateShapePositions(); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes @@ -95,6 +95,17 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + void buildShapes(); + void updateShapePositions(); + void updateShapePositionsLegacy(); + + void computeBoundingShape(const FBXGeometry& geometry); + void renderBoundingCollisionShapes(float alpha); + float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } + const CapsuleShape& getBoundingShape() const { return _boundingShape; } + + void resetShapePositions(); // DEBUG method + void renderRagdoll(); protected: @@ -121,6 +132,9 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); Avatar* _owningAvatar; + + CapsuleShape _boundingShape; + glm::vec3 _boundingShapeLocalOffset; }; #endif // hifi_SkeletonModel_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5d871f2c9e..2057f3248b 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -33,7 +33,6 @@ static int vec3VectorTypeId = qRegisterMetaType >(); Model::Model(QObject* parent) : QObject(parent), - PhysicalEntity(PhysicalEntity::ENTITY_MODEL), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), _scaleToFitLargestDimension(0.0f), @@ -42,8 +41,6 @@ Model::Model(QObject* parent) : _snappedToCenter(false), _rootIndex(-1), //_enableCollisionShapes(false), - _boundingShape(), - _boundingShapeLocalOffset(0.0f), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com") { @@ -793,308 +790,11 @@ AnimationHandlePointer Model::createAnimationHandle() { // virtual override from PhysicalEntity void Model::buildShapes() { - if (!_geometry || _rootIndex == -1) { - return; - } - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - 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]; - - 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 - 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); - } - } - - // 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(); -} - -void Model::computeBoundingShape(const FBXGeometry& geometry) { - // compute default joint transforms and rotations - // (in local frame, ignoring Model translation and rotation) - int numJoints = geometry.joints.size(); - QVector transforms; - transforms.fill(glm::mat4(), numJoints); - QVector finalRotations; - finalRotations.fill(glm::quat(), numJoints); - - QVector 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->setCenter(localPosition); - shape->setRotation(finalRotations[i] * joint.shapeRotation); - float distance = glm::length(localPosition) + shape->getBoundingRadius(); - if (distance > _boundingRadius) { - _boundingRadius = distance; - } - } - - // compute bounding box - Extents totalExtents; - totalExtents.reset(); - totalExtents.addPoint(glm::vec3(0.0f)); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - Extents shapeExtents; - shapeExtents.reset(); - glm::vec3 localPosition = shape->getCenter(); - int type = shape->getType(); - if (type == Shape::CAPSULE_SHAPE) { - // add the two furthest surface points of the capsule - CapsuleShape* capsule = static_cast(shape); - glm::vec3 axis; - capsule->computeNormalizedAxis(axis); - float radius = capsule->getRadius(); - float halfHeight = capsule->getHalfHeight(); - axis = halfHeight * axis + glm::vec3(radius); - - shapeExtents.addPoint(localPosition + axis); - shapeExtents.addPoint(localPosition - axis); - totalExtents.addExtents(shapeExtents); - } else if (type == Shape::SPHERE_SHAPE) { - float radius = shape->getBoundingRadius(); - glm::vec3 axis = glm::vec3(radius); - shapeExtents.addPoint(localPosition + axis); - shapeExtents.addPoint(localPosition - axis); - totalExtents.addExtents(shapeExtents); - } - } - - // compute bounding shape parameters - // NOTE: we assume that the longest side of totalExtents is the yAxis... - glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; - // ... and assume the radius is half the RMS of the X and Z sides: - float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - _boundingShape.setRadius(capsuleRadius); - _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); - _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); -} - -void Model::resetShapePositions() { - // DEBUG method. - // Moves shapes to the joint default locations for debug visibility into - // how the bounding shape is computed. - - if (!_geometry || _rootIndex == -1 || _shapes.isEmpty()) { - // geometry or joints have not yet been created - return; - } - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty() || _shapes.size() != geometry.joints.size()) { - return; - } - - // The shapes are moved to their default positions in computeBoundingShape(). - computeBoundingShape(geometry); - - // Then we move them into world frame for rendering at the Model's location. - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (shape) { - shape->setCenter(_translation + _rotation * shape->getCenter()); - shape->setRotation(_rotation * shape->getRotation()); - } - } - _boundingShape.setCenter(_translation + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); + // TODO: figure out how to load/build collision shapes for general models } void Model::updateShapePositions() { - if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { - glm::vec3 rootPosition(0.0f); - _boundingRadius = 0.0f; - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - // shape position and rotation need to be in world-frame - glm::quat stateRotation = state.getRotation(); - glm::vec3 shapeOffset = uniformScale * (stateRotation * joint.shapePosition); - glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); - Shape* shape = _shapes[i]; - if (shape) { - shape->setCenter(worldPosition); - shape->setRotation(_rotation * stateRotation * joint.shapeRotation); - float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); - if (distance > _boundingRadius) { - _boundingRadius = distance; - } - } - if (joint.parentIndex == -1) { - rootPosition = worldPosition; - } - } - _shapesAreDirty = false; - _boundingShape.setCenter(rootPosition + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); - } -} - -bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - const glm::vec3 relativeOrigin = origin - _translation; - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - float minDistance = FLT_MAX; - float radiusScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; - glm::vec3 end = _translation + _rotation * _jointStates[i].getPosition(); - float endRadius = joint.boneRadius * radiusScale; - glm::vec3 start = end; - float startRadius = joint.boneRadius * radiusScale; - if (joint.parentIndex != -1) { - start = _translation + _rotation * _jointStates[joint.parentIndex].getPosition(); - startRadius = geometry.joints[joint.parentIndex].boneRadius * radiusScale; - } - // for now, use average of start and end radii - float capsuleDistance; - if (findRayCapsuleIntersection(relativeOrigin, direction, start, end, - (startRadius + endRadius) / 2.0f, capsuleDistance)) { - minDistance = qMin(minDistance, capsuleDistance); - } - } - if (minDistance < FLT_MAX) { - distance = minDistance; - return true; - } - return false; -} - -bool Model::findCollisions(const QVector shapes, CollisionList& collisions) { - bool collided = false; - for (int i = 0; i < shapes.size(); ++i) { - const Shape* theirShape = shapes[i]; - if (!theirShape) { - continue; - } - for (int j = 0; j < _shapes.size(); ++j) { - const Shape* ourShape = _shapes[j]; - if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { - collided = true; - } - } - } - return collided; -} - -bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, - CollisionList& collisions, int skipIndex) { - bool collided = false; - SphereShape sphere(sphereRadius, sphereCenter); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - const FBXJoint& joint = geometry.joints[i]; - if (joint.parentIndex != -1) { - if (skipIndex != -1) { - int ancestorIndex = joint.parentIndex; - do { - if (ancestorIndex == skipIndex) { - goto outerContinue; - } - ancestorIndex = geometry.joints[ancestorIndex].parentIndex; - - } while (ancestorIndex != -1); - } - } - if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - outerContinue: ; - } - return collided; -} - -bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { - bool collided = false; - PlaneShape planeShape(plane); - for (int i = 0; i < _shapes.size(); i++) { - if (_shapes[i] && ShapeCollider::collideShapes(&planeShape, _shapes[i], collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - } - return collided; + // TODO: implement this when we know how to build shapes for regular Models } class Blender : public QRunnable { @@ -1441,41 +1141,6 @@ void Model::renderJointCollisionShapes(float alpha) { glPopMatrix(); } -void Model::renderBoundingCollisionShapes(float alpha) { - if (_shapes.isEmpty()) { - // the bounding shape has not been propery computed - // so no need to render it - return; - } - glPushMatrix(); - - Application::getInstance()->loadTranslatedViewMatrix(_translation); - - // draw a blue sphere at the capsule endpoint - glm::vec3 endPoint; - _boundingShape.getEndPoint(endPoint); - endPoint = endPoint - _translation; - glTranslatef(endPoint.x, endPoint.y, endPoint.z); - glColor4f(0.6f, 0.6f, 0.8f, alpha); - glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); - - // draw a yellow sphere at the capsule startpoint - glm::vec3 startPoint; - _boundingShape.getStartPoint(startPoint); - startPoint = startPoint - _translation; - glm::vec3 axis = endPoint - startPoint; - glTranslatef(-axis.x, -axis.y, -axis.z); - glColor4f(0.8f, 0.8f, 0.6f, alpha); - glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); - - // draw a green cylinder between the two points - glm::vec3 origin(0.0f); - glColor4f(0.6f, 0.8f, 0.6f, alpha); - Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius()); - - glPopMatrix(); -} - void Model::setBlendedVertices(const QVector& vertices, const QVector& normals) { if (_blendedVertexBuffers.isEmpty()) { return; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 20612a0e21..8851f42eb1 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -132,34 +131,15 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } - // virtual override from PhysicalEntity + // virtual overrides from PhysicalEntity virtual void buildShapes(); - - void resetShapePositions(); // DEBUG method virtual void updateShapePositions(); void renderJointCollisionShapes(float alpha); - void renderBoundingCollisionShapes(float alpha); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - - /// \param shapes list of pointers shapes to test against Model - /// \param collisions list to store collision results - /// \return true if at least one shape collided agains Model - bool findCollisions(const QVector shapes, CollisionList& collisions); - - bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, - CollisionList& collisions, int skipIndex = -1); - - bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - - float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } - /// Sets blended vertices computed in a separate thread. void setBlendedVertices(const QVector& vertices, const QVector& normals); - const CapsuleShape& getBoundingShape() const { return _boundingShape; } - protected: QSharedPointer _geometry; @@ -176,9 +156,6 @@ protected: QVector _jointStates; - CapsuleShape _boundingShape; - glm::vec3 _boundingShapeLocalOffset; - class MeshState { public: QVector clusterMatrices; @@ -222,8 +199,6 @@ protected: /// first free ancestor. float getLimbLength(int jointIndex) const; - void computeBoundingShape(const FBXGeometry& geometry); - private: friend class AnimationHandle; diff --git a/libraries/shared/src/PhysicalEntity.cpp b/libraries/shared/src/PhysicalEntity.cpp index 3084c31529..602708d4ad 100644 --- a/libraries/shared/src/PhysicalEntity.cpp +++ b/libraries/shared/src/PhysicalEntity.cpp @@ -11,9 +11,9 @@ #include "PhysicalEntity.h" #include "Shape.h" +#include "ShapeCollider.h" -PhysicalEntity::PhysicalEntity(EntityType type) : - _entityType(type), +PhysicalEntity::PhysicalEntity() : _translation(0.0f), _rotation(), _boundingRadius(0.0f), @@ -61,4 +61,93 @@ void PhysicalEntity::clearShapes() { _shapes.clear(); } +bool PhysicalEntity::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + /* TODO: Andrew to make this work + int numShapes = _shapes.size(); + float minDistance = FLT_MAX; + for (int j = 0; j < numShapes; ++j) { + const Shape* shape = _shapes[j]; + float thisDistance = FLT_MAX; + if (shape && ShapeCollider::findRayIntersection(ourShape, origin, direction, thisDistance)) { + if (thisDistance < minDistance) { + minDistance = thisDistance; + } + } + } + if (minDistance < FLT_MAX) { + distance = minDistance; + return true; + } + */ + return false; +} +bool PhysicalEntity::findCollisions(const QVector shapes, CollisionList& collisions) { + bool collided = false; + int numTheirShapes = shapes.size(); + for (int i = 0; i < numTheirShapes; ++i) { + const Shape* theirShape = shapes[i]; + if (!theirShape) { + continue; + } + int numOurShapes = _shapes.size(); + for (int j = 0; j < numOurShapes; ++j) { + const Shape* ourShape = _shapes[j]; + if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { + collided = true; + } + } + } + return collided; +} + +bool PhysicalEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, + CollisionList& collisions, int skipIndex) { + bool collided = false; + // TODO: Andrew to implement this or make it unecessary + /* + SphereShape sphere(sphereRadius, sphereCenter); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; + if (!shape) { + continue; + } + const FBXJoint& joint = geometry.joints[i]; + if (joint.parentIndex != -1) { + if (skipIndex != -1) { + int ancestorIndex = joint.parentIndex; + do { + if (ancestorIndex == skipIndex) { + goto outerContinue; + } + ancestorIndex = geometry.joints[ancestorIndex].parentIndex; + + } while (ancestorIndex != -1); + } + } + if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { + CollisionInfo* collision = collisions.getLastCollision(); + collision->_data = (void*)(this); + collision->_intData = i; + collided = true; + } + outerContinue: ; + } + */ + return collided; +} + +bool PhysicalEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { + bool collided = false; + PlaneShape planeShape(plane); + for (int i = 0; i < _shapes.size(); i++) { + if (_shapes[i] && ShapeCollider::collideShapes(&planeShape, _shapes[i], collisions)) { + CollisionInfo* collision = collisions.getLastCollision(); + collision->_data = (void*)(this); + collision->_intData = i; + collided = true; + } + } + return collided; +} diff --git a/libraries/shared/src/PhysicalEntity.h b/libraries/shared/src/PhysicalEntity.h index 22f2bff67b..9e95449812 100644 --- a/libraries/shared/src/PhysicalEntity.h +++ b/libraries/shared/src/PhysicalEntity.h @@ -17,6 +17,8 @@ #include #include +#include "CollisionInfo.h" + class Shape; // PhysicalEntity is the base class for anything that owns one or more Shapes that collide in a @@ -26,12 +28,7 @@ class Shape; class PhysicalEntity { public: - enum EntityType { - ENTITY_UNKNOWN, - ENTITY_MODEL, - }; - - PhysicalEntity(EntityType type = ENTITY_UNKNOWN); + PhysicalEntity(); virtual ~PhysicalEntity() {} void setTranslation(const glm::vec3& translation); @@ -41,8 +38,6 @@ public: const glm::quat& getRotation() const { return _rotation; } float getBoundingRadius() const { return _boundingRadius; } - EntityType getEntityType() const { return _entityType; } - void setShapeBackPointers(); void setEnableShapes(bool enable); @@ -50,8 +45,12 @@ public: virtual void buildShapes() = 0; virtual void clearShapes(); + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + bool findCollisions(const QVector shapes, CollisionList& collisions); + bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions, int skipIndex); + bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); + protected: - EntityType _entityType; glm::vec3 _translation; glm::quat _rotation; float _boundingRadius;