diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index da0d4cdc8f..ad83d2b926 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -454,7 +454,7 @@ void Application::paintGL() { bool eyeRelativeCamera = false; if (_rearMirrorTools->getZoomLevel() == BODY) { _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar.getScale()); - _mirrorCamera.setTargetPosition(_myAvatar.getChestJointPosition()); + _mirrorCamera.setTargetPosition(_myAvatar.getChestPosition()); } else { // HEAD zoom level _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar.getScale()); if (_myAvatar.getSkeletonModel().isActive() && _myAvatar.getHead().getFaceModel().isActive()) { @@ -2349,7 +2349,7 @@ void Application::updateTransmitter(float deltaTime) { // no transmitter drive implies transmitter pick if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { - _transmitterPickStart = _myAvatar.getChestJointPosition(); + _transmitterPickStart = _myAvatar.getChestPosition(); glm::vec3 direction = _myAvatar.getOrientation() * glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT; @@ -2973,7 +2973,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glEnable(GL_DEPTH_TEST); // Enable to show line from me to the voxel I am touching - //renderLineToTouchedVoxel(); //renderThrustAtVoxel(_voxelThrust); if (!selfAvatarOnly) { @@ -3544,20 +3543,6 @@ void Application::renderThrustAtVoxel(const glm::vec3& thrust) { } } -void Application::renderLineToTouchedVoxel() { - // Draw a teal line to the voxel I am currently dragging on - if (_mousePressed) { - glColor3f(0, 1, 1); - glLineWidth(2.0f); - glBegin(GL_LINES); - glm::vec3 voxelTouched = getMouseVoxelWorldCoordinates(_mouseVoxelDragging); - glVertex3f(voxelTouched.x, voxelTouched.y, voxelTouched.z); - glm::vec3 headPosition = _myAvatar.getHeadJointPosition(); - glVertex3fv(&headPosition.x); - glEnd(); - } -} - glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { float horizontalScale = _glWidget->width() / 2.0f; diff --git a/interface/src/Application.h b/interface/src/Application.h index 0a2b8b9c2d..56916afa7c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -240,7 +240,6 @@ private slots: void setFullscreen(bool fullscreen); void renderThrustAtVoxel(const glm::vec3& thrust); - void renderLineToTouchedVoxel(); void renderCoverageMap(); void renderCoverageMapsRecursively(CoverageMap* map); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 894f448fe6..ae0869cfd1 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -370,7 +370,7 @@ void Audio::handleAudioInput() { if (audioMixer && nodeList->getNodeActiveSocketOrPing(audioMixer.data())) { MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); - glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition(); + glm::vec3 headPosition = interfaceAvatar->getHead().getPosition(); glm::quat headOrientation = interfaceAvatar->getHead().getOrientation(); // we need the amount of bytes in the buffer + 1 for type diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ee5cdd294a..8f9dcab9ee 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -89,7 +89,6 @@ Avatar::Avatar(Node* owningNode) : _thrust(0.0f, 0.0f, 0.0f), _speed(0.0f), _leanScale(0.5f), - _pelvisFloatingHeight(0.0f), _scale(1.0f), _worldUpDirection(DEFAULT_UP_DIRECTION), _mouseRayOrigin(0.0f, 0.0f, 0.0f), @@ -105,12 +104,6 @@ Avatar::Avatar(Node* owningNode) : // give the pointer to our head to inherited _headData variable from AvatarData _headData = &_head; _handData = &_hand; - - _height = 0.0f; // _skeleton.getHeight(); - - _pelvisFloatingHeight = 0.0f; // _skeleton.getPelvisFloatingHeight(); - _pelvisToHeadLength = 0.0f; // _skeleton.getPelvisToHeadLength(); - } @@ -131,6 +124,12 @@ void Avatar::init() { _initialized = true; } +glm::vec3 Avatar::getChestPosition() const { + // for now, let's just assume that the "chest" is halfway between the root and the neck + glm::vec3 neckPosition; + return _skeletonModel.getNeckPosition(neckPosition) ? (_position + neckPosition) * 0.5f : _position; +} + glm::quat Avatar::getOrientation() const { return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll))); } @@ -197,14 +196,15 @@ void Avatar::render(bool forceRenderHead) { // render sphere when far away const float MAX_ANGLE = 10.f; - glm::vec3 delta = _height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f; + float height = getHeight(); + glm::vec3 delta = height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f; float angle = abs(angleBetween(toTarget + delta, toTarget - delta)); if (angle < MAX_ANGLE) { glColor4f(0.5f, 0.8f, 0.8f, 1.f - angle / MAX_ANGLE); glPushMatrix(); glTranslatef(_position.x, _position.y, _position.z); - glScalef(_height / 2.f, _height / 2.f, _height / 2.f); + glScalef(height / 2.f, height / 2.f, height / 2.f); glutSolidSphere(1.2f + _head.getAverageLoudness() * .0005f, 20, 20); glPopMatrix(); } @@ -421,10 +421,18 @@ void Avatar::setScale(const float scale) { _scale < _targetScale * (1.f + RESCALING_TOLERANCE)) { _scale = _targetScale; } - - _height = 0.0f; // _skeleton.getHeight(); - - _pelvisFloatingHeight = 0.0f; // _skeleton.getPelvisFloatingHeight(); - _pelvisToHeadLength = 0.0f; // _skeleton.getPelvisToHeadLength(); +} + +float Avatar::getHeight() const { + Extents extents = _skeletonModel.getBindExtents(); + return extents.maximum.y - extents.minimum.y; +} + +float Avatar::getPelvisFloatingHeight() const { + return -_skeletonModel.getBindExtents().minimum.y; +} + +float Avatar::getPelvisToHeadLength() const { + return glm::distance(_position, _head.getPosition()); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 85fc55acc0..add653032a 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -143,8 +143,7 @@ public: bool isInitialized() const { return _initialized; } SkeletonModel& getSkeletonModel() { return _skeletonModel; } float getHeadYawRate() const { return _head.yawRate; } - const glm::vec3& getHeadJointPosition() const { return _position; } - const glm::vec3& getChestJointPosition() const { return _position; } + glm::vec3 getChestPosition() const; float getScale() const { return _scale; } const glm::vec3& getVelocity() const { return _velocity; } Head& getHead() { return _head; } @@ -190,10 +189,7 @@ protected: glm::vec3 _thrust; float _speed; float _leanScale; - float _pelvisFloatingHeight; - float _pelvisToHeadLength; float _scale; - float _height; glm::vec3 _worldUpDirection; glm::vec3 _mouseRayOrigin; glm::vec3 _mouseRayDirection; @@ -209,6 +205,9 @@ protected: glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void setScale(const float scale); + float getHeight() const; + float getPelvisFloatingHeight() const; + float getPelvisToHeadLength() const; private: // privatize copy constructor and assignment operator to avoid copying diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 13a1fca611..50816d0dcf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -58,8 +58,6 @@ MyAvatar::MyAvatar(Node* owningNode) : for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } - - _collisionRadius = _height * COLLISION_RADIUS_SCALE; } void MyAvatar::reset() { @@ -122,7 +120,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { _collisionRadius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f)); _collisionRadius *= COLLISION_RADIUS_SCALAR; } else { - _collisionRadius = _height * COLLISION_RADIUS_SCALE; + _collisionRadius = getHeight() * COLLISION_RADIUS_SCALE; } updateCollisionWithEnvironment(deltaTime); @@ -515,7 +513,7 @@ float MyAvatar::getAbsoluteHeadYaw() const { } glm::vec3 MyAvatar::getUprightHeadPosition() const { - return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f); + return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); } void MyAvatar::renderBody(bool forceRenderHead) { @@ -698,9 +696,10 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime) { const float ENVIRONMENT_SURFACE_DAMPING = 0.01f; const float ENVIRONMENT_COLLISION_FREQUENCY = 0.05f; glm::vec3 penetration; + float pelvisFloatingHeight = getPelvisFloatingHeight(); if (Application::getInstance()->getEnvironment()->findCapsulePenetration( - _position - up * (_pelvisFloatingHeight - radius), - _position + up * (_height - _pelvisFloatingHeight + radius), radius, penetration)) { + _position - up * (pelvisFloatingHeight - radius), + _position + up * (getHeight() - pelvisFloatingHeight + radius), radius, penetration)) { _lastCollisionPosition = _position; updateCollisionSound(penetration, deltaTime, ENVIRONMENT_COLLISION_FREQUENCY); applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING); @@ -714,9 +713,10 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime) { const float VOXEL_DAMPING = 0.0f; const float VOXEL_COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; + float pelvisFloatingHeight = getPelvisFloatingHeight(); if (Application::getInstance()->getVoxels()->findCapsulePenetration( - _position - glm::vec3(0.0f, _pelvisFloatingHeight - radius, 0.0f), - _position + glm::vec3(0.0f, _height - _pelvisFloatingHeight + radius, 0.0f), radius, penetration)) { + _position - glm::vec3(0.0f, pelvisFloatingHeight - radius, 0.0f), + _position + glm::vec3(0.0f, getHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) { _lastCollisionPosition = _position; updateCollisionSound(penetration, deltaTime, VOXEL_COLLISION_FREQUENCY); applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING); diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 4ddc969510..5d27233f5e 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1194,6 +1194,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); } + geometry.bindExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX); + geometry.bindExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX); + QVariantHash springs = mapping.value("spring").toHash(); QVariant defaultSpring = springs.value("default"); for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { @@ -1286,6 +1289,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) FBXJoint& joint = geometry.joints[fbxCluster.jointIndex]; joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); joint.bindTransform = cluster.transformLink; + + // update the bind pose extents + glm::vec3 bindTranslation = extractTranslation(geometry.offset * joint.bindTransform); + geometry.bindExtents.minimum = glm::min(geometry.bindExtents.minimum, bindTranslation); + geometry.bindExtents.maximum = glm::max(geometry.bindExtents.maximum, bindTranslation); } } diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 45410500d9..d700439460 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -121,6 +121,13 @@ public: glm::vec3 scale; }; +class Extents { +public: + + glm::vec3 minimum; + glm::vec3 maximum; +}; + /// A set of meshes extracted from an FBX document. class FBXGeometry { public: @@ -145,12 +152,14 @@ public: QVector rightFingerJointIndices; QVector leftFingertipJointIndices; - QVector rightFingertipJointIndices; + QVector rightFingertipJointIndices; glm::vec3 palmDirection; glm::vec3 neckPivot; + Extents bindExtents; + QVector attachments; }; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 3eb1256f0f..44831966f9 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -460,6 +460,15 @@ bool Model::render(float alpha) { return true; } +Extents Model::getBindExtents() const { + if (!isActive()) { + return Extents(); + } + const Extents& bindExtents = _geometry->getFBXGeometry().bindExtents; + Extents scaledExtents = { bindExtents.minimum * _scale, bindExtents.maximum * _scale }; + return scaledExtents; +} + int Model::getParentJointIndex(int jointIndex) const { return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).parentIndex : -1; } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index fc3a0687b8..bd18325475 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -54,6 +54,12 @@ public: Q_INVOKABLE void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } + /// Returns the extents of the model in its bind pose. + Extents getBindExtents() const; + + /// Returns a reference to the shared geometry. + const QSharedPointer& getGeometry() const { return _geometry; } + /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; }