From 540649071970e5a2f3f25609af1768ecac7f6b3a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 28 Mar 2014 11:00:30 -0700 Subject: [PATCH 1/9] rename _shapes -> _jointShapes also stubbed Model::createBoundingShape() --- interface/src/avatar/SkeletonModel.cpp | 8 +++--- interface/src/renderer/Model.cpp | 37 +++++++++++++++----------- interface/src/renderer/Model.h | 5 ++-- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 3c5f5cae6a..9d67709fde 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -63,7 +63,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { - if (jointIndex < 0 || jointIndex >= int(_shapes.size())) { + if (jointIndex < 0 || jointIndex >= int(_jointShapes.size())) { return; } if (jointIndex == getLeftHandJointIndex() @@ -75,16 +75,16 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) int parentIndex = joint.parentIndex; if (i == jointIndex) { // this shape is the hand - shapes.push_back(_shapes[i]); + shapes.push_back(_jointShapes[i]); if (parentIndex != -1) { // also add the forearm - shapes.push_back(_shapes[parentIndex]); + shapes.push_back(_jointShapes[parentIndex]); } } else { while (parentIndex != -1) { if (parentIndex == jointIndex) { // this shape is a child of the hand - shapes.push_back(_shapes[i]); + shapes.push_back(_jointShapes[i]); break; } parentIndex = geometry.joints[parentIndex].parentIndex; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index df5560a4b1..ceddcd009a 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -136,13 +136,13 @@ void Model::reset() { } void Model::clearShapes() { - for (int i = 0; i < _shapes.size(); ++i) { - delete _shapes[i]; + for (int i = 0; i < _jointShapes.size(); ++i) { + delete _jointShapes[i]; } - _shapes.clear(); + _jointShapes.clear(); } -void Model::createCollisionShapes() { +void Model::createJointCollisionShapes() { clearShapes(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); float uniformScale = extractUniformScale(_scale); @@ -156,16 +156,21 @@ void Model::createCollisionShapes() { float halfHeight = 0.5f * uniformScale * joint.distanceToParent; CapsuleShape* shape = new CapsuleShape(radius, halfHeight); shape->setPosition(position); - _shapes.push_back(shape); + _jointShapes.push_back(shape); } else { SphereShape* shape = new SphereShape(radius, position); - _shapes.push_back(shape); + _jointShapes.push_back(shape); } } } +void Model::createBoundingShape() { + //const FBXGeometry& geometry = _geometry->getFBXGeometry(); + //float uniformScale = extractUniformScale(_scale); +} + void Model::updateShapePositions() { - if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { + if (_shapesAreDirty && _jointShapes.size() == _jointStates.size()) { _boundingRadius = 0.f; float uniformScale = extractUniformScale(_scale); const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -174,8 +179,8 @@ void Model::updateShapePositions() { // shape position and rotation need to be in world-frame glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition); glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation; - _shapes[i]->setPosition(worldPosition); - _shapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation); + _jointShapes[i]->setPosition(worldPosition); + _jointShapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation); float distance2 = glm::distance2(worldPosition, _translation); if (distance2 > _boundingRadius) { _boundingRadius = distance2; @@ -401,8 +406,8 @@ bool Model::findCollisions(const QVector shapes, CollisionList& co bool collided = false; for (int i = 0; i < shapes.size(); ++i) { const Shape* theirShape = shapes[i]; - for (int j = 0; j < _shapes.size(); ++j) { - const Shape* ourShape = _shapes[j]; + for (int j = 0; j < _jointShapes.size(); ++j) { + const Shape* ourShape = _jointShapes[j]; if (ShapeCollider::shapeShape(theirShape, ourShape, collisions)) { collided = true; } @@ -417,7 +422,7 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi updateShapePositions(); SphereShape sphere(sphereRadius, sphereCenter); const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _shapes.size(); i++) { + for (int i = 0; i < _jointShapes.size(); i++) { const FBXJoint& joint = geometry.joints[i]; if (joint.parentIndex != -1) { if (skipIndex != -1) { @@ -431,7 +436,7 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi } while (ancestorIndex != -1); } } - if (ShapeCollider::shapeShape(&sphere, _shapes[i], collisions)) { + if (ShapeCollider::shapeShape(&sphere, _jointShapes[i], collisions)) { CollisionInfo* collision = collisions.getLastCollision(); collision->_type = MODEL_COLLISION; collision->_data = (void*)(this); @@ -578,7 +583,7 @@ void Model::simulate(float deltaTime, bool fullUpdate, const QVector _attachments.append(model); } fullUpdate = true; - createCollisionShapes(); + createJointCollisionShapes(); } // exit early if we don't have to perform a full update @@ -825,10 +830,10 @@ void Model::renderCollisionProxies(float alpha) { Application::getInstance()->loadTranslatedViewMatrix(_translation); updateShapePositions(); const int BALL_SUBDIVISIONS = 10; - for (int i = 0; i < _shapes.size(); i++) { + for (int i = 0; i < _jointShapes.size(); i++) { glPushMatrix(); - Shape* shape = _shapes[i]; + Shape* shape = _jointShapes[i]; if (shape->getType() == Shape::SPHERE_SHAPE) { // shapes are stored in world-frame, so we have to transform into model frame diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 3fdf1cd291..0b05fab2bf 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -55,7 +55,8 @@ public: void init(); void reset(); void clearShapes(); - void createCollisionShapes(); + void createJointCollisionShapes(); + void createBoundingShape(); void updateShapePositions(); void simulate(float deltaTime, bool fullUpdate = true); bool render(float alpha = 1.0f, bool forShadowMap = false); @@ -203,7 +204,7 @@ protected: bool _shapesAreDirty; QVector _jointStates; - QVector _shapes; + QVector _jointShapes; class MeshState { public: From 1bbdc9d78b771ceafd9679c3fc02dafddd66d485 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Apr 2014 09:25:25 -0700 Subject: [PATCH 2/9] Adding bounding capsule shape for Model Also: Cleaned up the automatic geometry LOD loading Removed staticExtents data member from FBXGeometry Split debug rendering of bounding shapes from joint shapes --- interface/interface_en.ts | 8 +- interface/src/Menu.cpp | 5 +- interface/src/Menu.h | 5 +- interface/src/avatar/Avatar.cpp | 25 +- interface/src/avatar/Avatar.h | 1 - interface/src/avatar/FaceModel.cpp | 6 +- interface/src/avatar/Hand.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 47 +-- interface/src/avatar/SkeletonModel.cpp | 12 +- interface/src/avatar/SkeletonModel.h | 8 +- interface/src/renderer/FBXReader.cpp | 12 +- interface/src/renderer/FBXReader.h | 5 +- interface/src/renderer/Model.cpp | 389 +++++++++++++++---------- interface/src/renderer/Model.h | 33 ++- 14 files changed, 337 insertions(+), 221 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index da8827d89d..ade5275c30 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 79b0a23ce5..d77f236300 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -282,8 +282,9 @@ Menu::Menu() : QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionProxies); - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionProxies); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cab5645304..cc5bb2c186 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -285,8 +285,9 @@ namespace MenuOption { const QString PlaySlaps = "Play Slaps"; const QString Preferences = "Preferences..."; const QString ReloadAllScripts = "Reload All Scripts"; - const QString RenderSkeletonCollisionProxies = "Skeleton Collision Proxies"; - const QString RenderHeadCollisionProxies = "Head Collision Proxies"; + const QString RenderSkeletonCollisionShapes = "Skeleton Collision Shapes"; + const QString RenderHeadCollisionShapes = "Head Collision Shapes"; + const QString RenderBoundingCollisionShapes = "Bounding Collision Shapes"; const QString ResetAvatarSize = "Reset Avatar Size"; const QString RunTimingTests = "Run Timing Tests"; const QString SettingsImport = "Import Settings"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 75b0a4c66a..b36baf15b1 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -56,8 +56,7 @@ Avatar::Avatar() : _owningAvatarMixer(), _collisionFlags(0), _initialized(false), - _shouldRenderBillboard(true), - _modelsDirty(true) + _shouldRenderBillboard(true) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -125,8 +124,7 @@ void Avatar::simulate(float deltaTime) { } glm::vec3 headPosition = _position; if (!_shouldRenderBillboard && inViewFrustum) { - _skeletonModel.simulate(deltaTime, _modelsDirty); - _modelsDirty = false; + _skeletonModel.simulate(deltaTime); _skeletonModel.getHeadPosition(headPosition); } Head* head = getHead(); @@ -210,11 +208,19 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { renderBody(renderMode); } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) { - _skeletonModel.renderCollisionProxies(0.7f); + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { + _skeletonModel.updateShapePositions(); + _skeletonModel.renderJointCollisionShapes(0.7f); } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionProxies)) { - getHead()->getFaceModel().renderCollisionProxies(0.7f); + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) { + getHead()->getFaceModel().updateShapePositions(); + getHead()->getFaceModel().renderJointCollisionShapes(0.7f); + } + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) { + _skeletonModel.updateShapePositions(); + _skeletonModel.renderBoundingCollisionShapes(0.7f); + getHead()->getFaceModel().updateShapePositions(); + getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f); } // quick check before falling into the code below: @@ -650,9 +656,6 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) { const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; - // note that we need to update our models - _modelsDirty = true; - return bytesRead; } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index f2ee400ba2..f6d5669859 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -192,7 +192,6 @@ private: bool _initialized; QScopedPointer _billboardTexture; bool _shouldRenderBillboard; - bool _modelsDirty; void renderBillboard(); diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 19120d10be..c483642d15 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -19,8 +19,8 @@ FaceModel::FaceModel(Head* owningHead) : } void FaceModel::simulate(float deltaTime) { - QVector newJointStates = updateGeometry(); - if (!isActive()) { + bool geometryIsUpToDate = updateGeometry(); + if (!geometryIsUpToDate) { return; } Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); @@ -42,7 +42,7 @@ void FaceModel::simulate(float deltaTime) { setPupilDilation(_owningHead->getPupilDilation()); setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients()); - Model::simulate(deltaTime, true, newJointStates); + Model::simulateInternal(deltaTime); } void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 750fae7b2a..4351a53ab4 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -253,7 +253,7 @@ void Hand::render(bool isMine) { _renderAlpha = 1.0; - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { // draw a green sphere at hand joint location, which is actually near the wrist) for (size_t i = 0; i < getNumPalms(); i++) { PalmData& palm = getPalms()[i]; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e18735e3e5..6e01ad3292 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -20,6 +20,11 @@ #include #include +#ifdef ANDREW_HACKERY +#include +#include +#endif ANDREW_HACKERY + #include "Application.h" #include "Audio.h" #include "Environment.h" @@ -867,6 +872,8 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float return false; } +static CollisionList bodyCollisions(16); + void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // Reset detector for nearest avatar _distanceToNearestAvatar = std::numeric_limits::max(); @@ -878,15 +885,6 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { updateShapePositions(); float myBoundingRadius = getBoundingRadius(); - /* TODO: Andrew to fix Avatar-Avatar body collisions - // HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis - // TODO: make the collision work without assuming avatar orientation - Extents myStaticExtents = _skeletonModel.getStaticExtents(); - glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum; - float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z); - float myCapsuleHeight = staticScale.y; - */ - foreach (const AvatarSharedPointer& avatarPointer, avatars) { Avatar* avatar = static_cast(avatarPointer.data()); if (static_cast(this) == avatar) { @@ -900,19 +898,26 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { } float theirBoundingRadius = avatar->getBoundingRadius(); if (distance < myBoundingRadius + theirBoundingRadius) { - /* TODO: Andrew to fix Avatar-Avatar body collisions - Extents theirStaticExtents = _skeletonModel.getStaticExtents(); - glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum; - float theirCapsuleRadius = 0.25f * (staticScale.x + staticScale.z); - float theirCapsuleHeight = staticScale.y; - - glm::vec3 penetration(0.f); - if (findAvatarAvatarPenetration(_position, myCapsuleRadius, myCapsuleHeight, - avatar->getPosition(), theirCapsuleRadius, theirCapsuleHeight, penetration)) { - // move the avatar out by half the penetration - setPosition(_position - 0.5f * penetration); +#ifdef ANDREW_HACKERY + QVector myShapes; + _skeletonModel.getBodyShapes(myShapes); + QVector theirShapes; + avatar->getSkeletonModel().getBodyShapes(theirShapes); + bodyCollisions.clear(); + foreach (const Shape* myShape, myShapes) { + foreach (const Shape* theirShape, theirShapes) { + ShapeCollider::shapeShape(myShape, theirShape, bodyCollisions); + if (bodyCollisions.size() > 0) { + std::cout << "adebug myPos = " << myShape->getPosition() + << " myRadius = " << myShape->getBoundingRadius() + << " theirPos = " << theirShape->getPosition() + << " theirRadius = " << theirShape->getBoundingRadius() + << std::endl; // adebug + std::cout << "adebug collision count = " << bodyCollisions.size() << std::endl; // adebug + } + } } - */ +#endif // ANDREW_HACKERY // collide our hands against them // TODO: make this work when we can figure out when the other avatar won't yeild diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 9d67709fde..0986021ac8 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -14,17 +14,17 @@ #include "Menu.h" #include "SkeletonModel.h" -SkeletonModel::SkeletonModel(Avatar* owningAvatar) : +SkeletonModel::SkeletonModel(Avatar* owningAvatar) : _owningAvatar(owningAvatar) { } -void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { +void SkeletonModel::simulate(float deltaTime) { setTranslation(_owningAvatar->getPosition()); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f))); const float MODEL_SCALE = 0.0006f; setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE); - Model::simulate(deltaTime, fullUpdate); + Model::simulate(deltaTime); if (!(isActive() && _owningAvatar->isMyAvatar())) { return; // only simulate for own avatar @@ -94,6 +94,12 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) } } +void SkeletonModel::getBodyShapes(QVector& shapes) const { + // for now we push a single bounding shape, + // but later we could push a subset of joint shapes + shapes.push_back(&_boundingShape); +} + class IndexValue { public: int index; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 213a53d9ed..60b12eb8f5 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -9,7 +9,6 @@ #ifndef __interface__SkeletonModel__ #define __interface__SkeletonModel__ - #include "renderer/Model.h" class Avatar; @@ -22,14 +21,17 @@ public: SkeletonModel(Avatar* owningAvatar); - void simulate(float deltaTime, bool fullUpdate = true); + void simulate(float deltaTime); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes void getHandShapes(int jointIndex, QVector& shapes) const; + /// \param shapes[out] list of shapes for body collisions + void getBodyShapes(QVector& shapes) const; + protected: - + void applyHandPosition(int jointIndex, const glm::vec3& position); void applyPalmData(int jointIndex, const QVector& fingerJointIndices, diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 53f4e04b0b..87b7d43938 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -41,6 +41,11 @@ bool Extents::containsPoint(const glm::vec3& point) const { && point.z >= minimum.z && point.z <= maximum.z); } +void Extents::addExtents(const Extents& extents) { + minimum = glm::min(minimum, extents.minimum); + maximum = glm::max(maximum, extents.maximum); +} + void Extents::addPoint(const glm::vec3& point) { minimum = glm::min(minimum, point); maximum = glm::max(maximum, point); @@ -1337,7 +1342,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } geometry.bindExtents.reset(); - geometry.staticExtents.reset(); geometry.meshExtents.reset(); for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { @@ -1505,8 +1509,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex]; jointShapeInfo.boneBegin = rotateMeshToJoint * (radiusScale * (boneBegin - boneEnd)); - bool jointIsStatic = joint.freeLineage.isEmpty(); - glm::vec3 jointTranslation = extractTranslation(geometry.offset * joint.bindTransform); float totalWeight = 0.0f; for (int j = 0; j < cluster.indices.size(); j++) { int oldIndex = cluster.indices.at(j); @@ -1528,10 +1530,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) jointShapeInfo.extents.addPoint(vertexInJointFrame); jointShapeInfo.averageVertex += vertexInJointFrame; ++jointShapeInfo.numVertices; - if (jointIsStatic) { - // expand the extents of static (nonmovable) joints - geometry.staticExtents.addPoint(vertex + jointTranslation); - } } // look for an unused slot in the weights vector diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 5f6a4f51ba..2847d2a971 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -30,6 +30,10 @@ public: /// set minimum and maximum to FLT_MAX and -FLT_MAX respectively void reset(); + /// \param extents another intance of extents + /// expand current limits to contain other extents + void addExtents(const Extents& extents); + /// \param point new point to compare against existing limits /// compare point to current limits and expand them if necessary to contain point void addPoint(const glm::vec3& point); @@ -174,7 +178,6 @@ public: glm::vec3 neckPivot; Extents bindExtents; - Extents staticExtents; Extents meshExtents; QVector attachments; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index ceddcd009a..8251b67b1e 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -32,9 +32,11 @@ Model::Model(QObject* parent) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), _shapesAreDirty(true), + _boundingRadius(0.f), + _boundingShape(), + _boundingShapeLocalOffset(0.f), _lodDistance(0.0f), - _pupilDilation(0.0f), - _boundingRadius(0.f) { + _pupilDilation(0.0f) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); } @@ -73,6 +75,44 @@ QVector Model::createJointStates(const FBXGeometry& geometry) state.rotation = joint.rotation; jointStates.append(state); } + + // compute transforms + // Unfortunately, the joints are not neccessarily in order from parents to children, + // so we must iterate over the list multiple times until all are set correctly. + QVector jointIsSet; + int numJoints = jointStates.size(); + jointIsSet.fill(false, numJoints); + int numJointsSet = 0; + int lastNumJointsSet = -1; + while (numJointsSet < numJoints && numJointsSet != lastNumJointsSet) { + lastNumJointsSet = numJointsSet; + for (int i = 0; i < numJoints; ++i) { + if (jointIsSet[i]) { + continue; + } + JointState& state = jointStates[i]; + const FBXJoint& joint = geometry.joints[i]; + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset); + glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation; + state.transform = baseTransform * geometry.offset * glm::translate(state.translation) * joint.preTransform * + glm::mat4_cast(combinedRotation) * joint.postTransform; + state.combinedRotation = _rotation * combinedRotation; + ++numJointsSet; + jointIsSet[i] = true; + } else if (jointIsSet[parentIndex]) { + const JointState& parentState = jointStates.at(parentIndex); + glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation; + state.transform = parentState.transform * glm::translate(state.translation) * joint.preTransform * + glm::mat4_cast(combinedRotation) * joint.postTransform; + state.combinedRotation = parentState.combinedRotation * combinedRotation; + ++numJointsSet; + jointIsSet[i] = true; + } + } + } + return jointStates; } @@ -135,65 +175,87 @@ void Model::reset() { } } -void Model::clearShapes() { - for (int i = 0; i < _jointShapes.size(); ++i) { - delete _jointShapes[i]; - } - _jointShapes.clear(); -} +// return 'true' if geometry is up to date +bool Model::updateGeometry() { + bool needToRebuild = false; -void Model::createJointCollisionShapes() { - clearShapes(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; - glm::vec3 meshCenter = _jointStates[i].combinedRotation * joint.shapePosition; - glm::vec3 position = _rotation * (extractTranslation(_jointStates[i].transform) + uniformScale * meshCenter) + _translation; - - float radius = uniformScale * joint.boneRadius; - if (joint.shapeType == Shape::CAPSULE_SHAPE) { - float halfHeight = 0.5f * uniformScale * joint.distanceToParent; - CapsuleShape* shape = new CapsuleShape(radius, halfHeight); - shape->setPosition(position); - _jointShapes.push_back(shape); - } else { - SphereShape* shape = new SphereShape(radius, position); - _jointShapes.push_back(shape); + if (_nextGeometry) { + _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis); + _nextGeometry->setLoadPriority(this, -_lodDistance); + _nextGeometry->ensureLoading(); + if (_nextGeometry->isLoaded()) { + applyNextGeometry(); + needToRebuild = true; } } -} + if (!_geometry) { + // geometry is not ready + return false; + } -void Model::createBoundingShape() { - //const FBXGeometry& geometry = _geometry->getFBXGeometry(); - //float uniformScale = extractUniformScale(_scale); -} + QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); + if (_geometry != geometry) { + // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above. + // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry. -void Model::updateShapePositions() { - if (_shapesAreDirty && _jointShapes.size() == _jointStates.size()) { - _boundingRadius = 0.f; - float uniformScale = extractUniformScale(_scale); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; - // shape position and rotation need to be in world-frame - glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition); - glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation; - _jointShapes[i]->setPosition(worldPosition); - _jointShapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation); - float distance2 = glm::distance2(worldPosition, _translation); - if (distance2 > _boundingRadius) { - _boundingRadius = distance2; + const FBXGeometry& newGeometry = geometry->getFBXGeometry(); + QVector newJointStates = createJointStates(newGeometry); + if (! _jointStates.isEmpty()) { + // copy the existing joint states + const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); + for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); + it != oldGeometry.jointIndices.constEnd(); it++) { + int oldIndex = it.value() - 1; + int newIndex = newGeometry.getJointIndex(it.key()); + if (newIndex != -1) { + newJointStates[newIndex] = _jointStates.at(oldIndex); + } } + } + deleteGeometry(); + _dilatedTextures.clear(); + _geometry = geometry; + _jointStates = newJointStates; + needToRebuild = true; + } else if (_jointStates.isEmpty()) { + const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); + if (fbxGeometry.joints.size() > 0) { + _jointStates = createJointStates(fbxGeometry); + needToRebuild = true; } - _boundingRadius = sqrtf(_boundingRadius); - _shapesAreDirty = false; } -} - -void Model::simulate(float deltaTime, bool fullUpdate) { - // update our LOD, then simulate - simulate(deltaTime, fullUpdate, updateGeometry()); + _geometry->setLoadPriority(this, -_lodDistance); + _geometry->ensureLoading(); + + if (needToRebuild) { + const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); + foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + MeshState state; + state.clusterMatrices.resize(mesh.clusters.size()); + _meshStates.append(state); + + QOpenGLBuffer buffer; + if (!mesh.blendshapes.isEmpty()) { + buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); + buffer.create(); + buffer.bind(); + buffer.allocate((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); + buffer.write(0, mesh.vertices.constData(), mesh.vertices.size() * sizeof(glm::vec3)); + buffer.write(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.constData(), + mesh.normals.size() * sizeof(glm::vec3)); + buffer.release(); + } + _blendedVertexBuffers.append(buffer); + } + foreach (const FBXAttachment& attachment, fbxGeometry.attachments) { + Model* model = new Model(this); + model->init(); + model->setURL(attachment.url); + _attachments.append(model); + } + createShapes(); + } + return true; } bool Model::render(float alpha, bool forShadowMap) { @@ -262,15 +324,6 @@ Extents Model::getBindExtents() const { return scaledExtents; } -Extents Model::getStaticExtents() const { - if (!isActive()) { - return Extents(); - } - const Extents& staticExtents = _geometry->getFBXGeometry().staticExtents; - Extents scaledExtents = { staticExtents.minimum * _scale, staticExtents.maximum * _scale }; - return scaledExtents; -} - bool Model::getJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; @@ -373,6 +426,90 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo } } +void Model::clearShapes() { + for (int i = 0; i < _jointShapes.size(); ++i) { + delete _jointShapes[i]; + } + _jointShapes.clear(); +} + +void Model::createShapes() { + clearShapes(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + float uniformScale = extractUniformScale(_scale); + glm::quat inverseRotation = glm::inverse(_rotation); + + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i); + } + + // joint shapes + Extents totalExtents; + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& joint = geometry.joints[i]; + + glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition); + glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation; + Extents shapeExtents; + + float radius = uniformScale * joint.boneRadius; + float halfHeight = 0.5f * uniformScale * joint.distanceToParent; + if (joint.shapeType == Shape::CAPSULE_SHAPE && halfHeight > EPSILON) { + CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); + capsule->setPosition(worldPosition); + capsule->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation); + _jointShapes.push_back(capsule); + + glm::vec3 endPoint; + capsule->getEndPoint(endPoint); + glm::vec3 startPoint; + capsule->getStartPoint(startPoint); + glm::vec3 axis = (halfHeight + radius) * glm::normalize(endPoint - startPoint); + shapeExtents.addPoint(inverseRotation * (worldPosition + axis - _translation)); + shapeExtents.addPoint(inverseRotation * (worldPosition - axis - _translation)); + } else { + SphereShape* sphere = new SphereShape(radius, worldPosition); + _jointShapes.push_back(sphere); + + glm::vec3 axis = glm::vec3(radius); + shapeExtents.addPoint(inverseRotation * (worldPosition + axis - _translation)); + shapeExtents.addPoint(inverseRotation * (worldPosition - axis - _translation)); + } + totalExtents.addExtents(shapeExtents); + } + + // bounding shape + // NOTE: we assume that the longest side of totalExtents is the yAxis + glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; + float capsuleRadius = 0.25f * (diagonal.x + diagonal.z); // half the average of x and z + _boundingShape.setRadius(capsuleRadius); + _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); + _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); +} + +void Model::updateShapePositions() { + if (_shapesAreDirty && _jointShapes.size() == _jointStates.size()) { + _boundingRadius = 0.f; + float uniformScale = extractUniformScale(_scale); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& joint = geometry.joints[i]; + // shape position and rotation need to be in world-frame + glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition); + glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation; + _jointShapes[i]->setPosition(worldPosition); + _jointShapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation); + float distance2 = glm::distance2(worldPosition, _translation); + if (distance2 > _boundingRadius) { + _boundingRadius = distance2; + } + } + _boundingRadius = sqrtf(_boundingRadius); + _shapesAreDirty = false; + } + _boundingShape.setPosition(_translation + _rotation * _boundingShapeLocalOffset); +} + 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(); @@ -419,7 +556,6 @@ bool Model::findCollisions(const QVector shapes, CollisionList& co bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions, int skipIndex) { bool collided = false; - updateShapePositions(); SphereShape sphere(sphereRadius, sphereCenter); const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointShapes.size(); i++) { @@ -448,45 +584,6 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi return collided; } -QVector Model::updateGeometry() { - QVector newJointStates; - if (_nextGeometry) { - _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis); - _nextGeometry->setLoadPriority(this, -_lodDistance); - _nextGeometry->ensureLoading(); - if (_nextGeometry->isLoaded()) { - applyNextGeometry(); - return newJointStates; - } - } - if (!_geometry) { - return newJointStates; - } - QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); - if (_geometry != geometry) { - if (!_jointStates.isEmpty()) { - // copy the existing joint states - const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); - const FBXGeometry& newGeometry = geometry->getFBXGeometry(); - newJointStates = createJointStates(newGeometry); - for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); - it != oldGeometry.jointIndices.constEnd(); it++) { - int oldIndex = it.value() - 1; - int newIndex = newGeometry.getJointIndex(it.key()); - if (newIndex != -1) { - newJointStates[newIndex] = _jointStates.at(oldIndex); - } - } - } - deleteGeometry(); - _dilatedTextures.clear(); - _geometry = geometry; - } - _geometry->setLoadPriority(this, -_lodDistance); - _geometry->ensureLoading(); - return newJointStates; -} - class Blender : public QRunnable { public: @@ -549,53 +646,23 @@ void Blender::run() { Q_ARG(const QVector&, vertices), Q_ARG(const QVector&, normals)); } -void Model::simulate(float deltaTime, bool fullUpdate, const QVector& newJointStates) { - if (!isActive()) { + +void Model::simulate(float deltaTime) { + bool geometryIsUpToDate = updateGeometry(); + if (!geometryIsUpToDate) { return; } - - // set up world vertices on first simulate after load - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (_jointStates.isEmpty()) { - _jointStates = newJointStates.isEmpty() ? createJointStates(geometry) : newJointStates; - foreach (const FBXMesh& mesh, geometry.meshes) { - MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); - _meshStates.append(state); - - QOpenGLBuffer buffer; - if (!mesh.blendshapes.isEmpty()) { - buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); - buffer.create(); - buffer.bind(); - buffer.allocate((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); - buffer.write(0, mesh.vertices.constData(), mesh.vertices.size() * sizeof(glm::vec3)); - buffer.write(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.constData(), - mesh.normals.size() * sizeof(glm::vec3)); - buffer.release(); - } - _blendedVertexBuffers.append(buffer); - } - foreach (const FBXAttachment& attachment, geometry.attachments) { - Model* model = new Model(this); - model->init(); - model->setURL(attachment.url); - _attachments.append(model); - } - fullUpdate = true; - createJointCollisionShapes(); - } - - // exit early if we don't have to perform a full update - if (!fullUpdate) { - return; - } - + simulateInternal(deltaTime); +} + +void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // update the attachment transforms and simulate them for (int i = 0; i < _attachments.size(); i++) { const FBXAttachment& attachment = geometry.attachments.at(i); @@ -641,7 +708,7 @@ void Model::updateJointState(int index) { state.transform = baseTransform * geometry.offset * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; state.combinedRotation = _rotation * combinedRotation; - + } else { const JointState& parentState = _jointStates.at(joint.parentIndex); if (index == geometry.leanJointIndex) { @@ -825,11 +892,11 @@ void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool cons state.rotation = newRotation; } -void Model::renderCollisionProxies(float alpha) { +const int BALL_SUBDIVISIONS = 10; + +void Model::renderJointCollisionShapes(float alpha) { glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); - updateShapePositions(); - const int BALL_SUBDIVISIONS = 10; for (int i = 0; i < _jointShapes.size(); i++) { glPushMatrix(); @@ -876,6 +943,36 @@ void Model::renderCollisionProxies(float alpha) { glPopMatrix(); } +void Model::renderBoundingCollisionShapes(float alpha) { + 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.f); + glColor4f(0.6f, 0.8f, 0.6f, alpha); + Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius()); + + glPopMatrix(); +} + bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const { if (collision._type == MODEL_COLLISION) { // the joint is pokable by a collision if it exists and is free to move diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 0b05fab2bf..205687baa9 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -12,6 +12,8 @@ #include #include +#include + #include "GeometryCache.h" #include "InterfaceConfig.h" #include "ProgramObject.h" @@ -54,13 +56,9 @@ public: void init(); void reset(); - void clearShapes(); - void createJointCollisionShapes(); - void createBoundingShape(); - void updateShapePositions(); - void simulate(float deltaTime, bool fullUpdate = true); + void simulate(float deltaTime); bool render(float alpha = 1.0f, bool forShadowMap = false); - + /// Sets the URL of the model to render. /// \param fallback the URL of a fallback model to render if the requested model fails to load /// \param retainCurrent if true, keep rendering the current model until the new one is loaded @@ -76,9 +74,6 @@ public: /// Returns the extents of the model in its bind pose. Extents getBindExtents() const; - /// Returns the extents of the unmovable joints of the model. - Extents getStaticExtents() const; - /// Returns a reference to the shared geometry. const QSharedPointer& getGeometry() const { return _geometry; } @@ -160,6 +155,12 @@ public: /// Returns the extended length from the right hand to its first free ancestor. float getRightArmLength() const; + void clearShapes(); + void createShapes(); + 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 @@ -170,8 +171,6 @@ public: bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions, int skipIndex = -1); - void renderCollisionProxies(float alpha); - /// \param collision details about the collisions /// \return true if the collision is against a moveable joint bool collisionHitsMoveableJoint(CollisionInfo& collision) const; @@ -206,6 +205,10 @@ protected: QVector _jointStates; QVector _jointShapes; + float _boundingRadius; + CapsuleShape _boundingShape; + glm::vec3 _boundingShapeLocalOffset; + class MeshState { public: QVector clusterMatrices; @@ -213,8 +216,8 @@ protected: QVector _meshStates; - QVector updateGeometry(); - void simulate(float deltaTime, bool fullUpdate, const QVector& newJointStates); + bool updateGeometry(); + void simulateInternal(float deltaTime); /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); @@ -249,6 +252,7 @@ private: void applyNextGeometry(); void deleteGeometry(); void renderMeshes(float alpha, bool forShadowMap, bool translucent); + QVector createJointStates(const FBXGeometry& geometry); QSharedPointer _baseGeometry; ///< reference required to prevent collection of base QSharedPointer _nextBaseGeometry; @@ -268,8 +272,6 @@ private: QVector _attachments; - float _boundingRadius; - static ProgramObject _program; static ProgramObject _normalMapProgram; static ProgramObject _shadowProgram; @@ -292,7 +294,6 @@ private: static SkinLocations _skinShadowLocations; static void initSkinProgram(ProgramObject& program, SkinLocations& locations); - static QVector createJointStates(const FBXGeometry& geometry); }; Q_DECLARE_METATYPE(QPointer) From 03d04e194d2ceff66e79322c3cba0035c557c3e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Apr 2014 10:39:58 -0700 Subject: [PATCH 3/9] Init bounding Extents for more correct shapes --- interface/src/renderer/Model.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 8251b67b1e..91b69d052c 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -445,12 +445,14 @@ void Model::createShapes() { // joint shapes Extents totalExtents; + totalExtents.reset(); for (int i = 0; i < _jointStates.size(); i++) { const FBXJoint& joint = geometry.joints[i]; glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition); glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation; Extents shapeExtents; + shapeExtents.reset(); float radius = uniformScale * joint.boneRadius; float halfHeight = 0.5f * uniformScale * joint.distanceToParent; From 9f9127b7165f0535fe63ad816bf94512a3f10cb9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Apr 2014 11:39:24 -0700 Subject: [PATCH 4/9] draw head bounding shape first --- interface/src/avatar/Avatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b36baf15b1..490daf4cab 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -217,10 +217,10 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } if (Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) { - _skeletonModel.updateShapePositions(); - _skeletonModel.renderBoundingCollisionShapes(0.7f); getHead()->getFaceModel().updateShapePositions(); getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f); + _skeletonModel.updateShapePositions(); + _skeletonModel.renderBoundingCollisionShapes(0.7f); } // quick check before falling into the code below: From 3d43956c111998eab97abb3731e195e9aa7ea86d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Apr 2014 11:39:40 -0700 Subject: [PATCH 5/9] bounding shape relative to model's root Joint --- interface/src/renderer/Model.cpp | 33 +++++++++++++++++++++++--------- interface/src/renderer/Model.h | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 91b69d052c..3a12d76626 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -435,13 +435,20 @@ void Model::clearShapes() { void Model::createShapes() { clearShapes(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - float uniformScale = extractUniformScale(_scale); - glm::quat inverseRotation = glm::inverse(_rotation); + if (_jointStates.isEmpty()) { + return; + } + + // make sure all the joints are updated correctly before we try to create their shapes for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + float uniformScale = extractUniformScale(_scale); + glm::quat inverseRotation = glm::inverse(_rotation); + glm::vec3 rootPosition(0.f); // joint shapes Extents totalExtents; @@ -454,6 +461,10 @@ void Model::createShapes() { Extents shapeExtents; shapeExtents.reset(); + if (joint.parentIndex == -1) { + rootPosition = worldPosition; + } + float radius = uniformScale * joint.boneRadius; float halfHeight = 0.5f * uniformScale * joint.distanceToParent; if (joint.shapeType == Shape::CAPSULE_SHAPE && halfHeight > EPSILON) { @@ -467,15 +478,15 @@ void Model::createShapes() { glm::vec3 startPoint; capsule->getStartPoint(startPoint); glm::vec3 axis = (halfHeight + radius) * glm::normalize(endPoint - startPoint); - shapeExtents.addPoint(inverseRotation * (worldPosition + axis - _translation)); - shapeExtents.addPoint(inverseRotation * (worldPosition - axis - _translation)); + shapeExtents.addPoint(worldPosition + axis); + shapeExtents.addPoint(worldPosition - axis); } else { SphereShape* sphere = new SphereShape(radius, worldPosition); _jointShapes.push_back(sphere); glm::vec3 axis = glm::vec3(radius); - shapeExtents.addPoint(inverseRotation * (worldPosition + axis - _translation)); - shapeExtents.addPoint(inverseRotation * (worldPosition - axis - _translation)); + shapeExtents.addPoint(worldPosition + axis); + shapeExtents.addPoint(worldPosition - axis); } totalExtents.addExtents(shapeExtents); } @@ -486,11 +497,12 @@ void Model::createShapes() { float capsuleRadius = 0.25f * (diagonal.x + diagonal.z); // half the average of x and z _boundingShape.setRadius(capsuleRadius); _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); - _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); + _boundingShapeLocalOffset = inverseRotation * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition); } void Model::updateShapePositions() { if (_shapesAreDirty && _jointShapes.size() == _jointStates.size()) { + glm::vec3 rootPosition(0.f); _boundingRadius = 0.f; float uniformScale = extractUniformScale(_scale); const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -505,11 +517,14 @@ void Model::updateShapePositions() { if (distance2 > _boundingRadius) { _boundingRadius = distance2; } + if (joint.parentIndex == -1) { + rootPosition = worldPosition; + } } _boundingRadius = sqrtf(_boundingRadius); _shapesAreDirty = false; + _boundingShape.setPosition(rootPosition + _rotation * _boundingShapeLocalOffset); } - _boundingShape.setPosition(_translation + _rotation * _boundingShapeLocalOffset); } bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 205687baa9..43eb7fda67 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -56,7 +56,7 @@ public: void init(); void reset(); - void simulate(float deltaTime); + virtual void simulate(float deltaTime); bool render(float alpha = 1.0f, bool forShadowMap = false); /// Sets the URL of the model to render. From 59bba99c6f49f8344a0cd340a1283a9d8459596d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Apr 2014 12:48:17 -0700 Subject: [PATCH 6/9] Resolve body-body collisions with other avatars --- interface/src/avatar/MyAvatar.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6e01ad3292..9710ad3b17 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -11,6 +11,7 @@ #include +#include #include #include @@ -20,10 +21,7 @@ #include #include -#ifdef ANDREW_HACKERY #include -#include -#endif ANDREW_HACKERY #include "Application.h" #include "Audio.h" @@ -873,6 +871,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float } static CollisionList bodyCollisions(16); +const float BODY_COLLISION_RESOLVE_TIMESCALE = 0.5f; // seconds void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // Reset detector for nearest avatar @@ -885,6 +884,8 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { updateShapePositions(); float myBoundingRadius = getBoundingRadius(); + const float BODY_COLLISION_RESOLVE_FACTOR = deltaTime / BODY_COLLISION_RESOLVE_TIMESCALE; + foreach (const AvatarSharedPointer& avatarPointer, avatars) { Avatar* avatar = static_cast(avatarPointer.data()); if (static_cast(this) == avatar) { @@ -898,26 +899,27 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { } float theirBoundingRadius = avatar->getBoundingRadius(); if (distance < myBoundingRadius + theirBoundingRadius) { -#ifdef ANDREW_HACKERY + // collide our body against theirs QVector myShapes; _skeletonModel.getBodyShapes(myShapes); QVector theirShapes; avatar->getSkeletonModel().getBodyShapes(theirShapes); bodyCollisions.clear(); + // TODO: add method to ShapeCollider for colliding lists of shapes foreach (const Shape* myShape, myShapes) { foreach (const Shape* theirShape, theirShapes) { ShapeCollider::shapeShape(myShape, theirShape, bodyCollisions); - if (bodyCollisions.size() > 0) { - std::cout << "adebug myPos = " << myShape->getPosition() - << " myRadius = " << myShape->getBoundingRadius() - << " theirPos = " << theirShape->getPosition() - << " theirRadius = " << theirShape->getBoundingRadius() - << std::endl; // adebug - std::cout << "adebug collision count = " << bodyCollisions.size() << std::endl; // adebug - } } } -#endif // ANDREW_HACKERY + glm::vec3 totalPenetration(0.f); + for (int j = 0; j < bodyCollisions.size(); ++j) { + CollisionInfo* collision = bodyCollisions.getCollision(j); + totalPenetration = addPenetrations(totalPenetration, collision->_penetration); + } + + if (glm::length2(totalPenetration) > EPSILON) { + setPosition(getPosition() - BODY_COLLISION_RESOLVE_FACTOR * totalPenetration); + } // collide our hands against them // TODO: make this work when we can figure out when the other avatar won't yeild From 515593e41cb1b0b45fc079817a5ee3c72fe0c546 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 1 Apr 2014 17:25:26 -0700 Subject: [PATCH 7/9] modify avatar thrust during collisions --- interface/src/avatar/MyAvatar.cpp | 35 +++++++++++++++++++++++++------ interface/src/avatar/MyAvatar.h | 1 + interface/src/renderer/Model.h | 1 + 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 249929d774..9dcfaa09ba 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -86,9 +86,9 @@ void MyAvatar::reset() { getHead()->reset(); getHand()->reset(); - setVelocity(glm::vec3(0,0,0)); - setThrust(glm::vec3(0,0,0)); - setOrientation(glm::quat(glm::vec3(0,0,0))); + setVelocity(glm::vec3(0.f)); + setThrust(glm::vec3(0.f)); + setOrientation(glm::quat(glm::vec3(0.f))); } void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) { @@ -677,6 +677,28 @@ void MyAvatar::updateThrust(float deltaTime) { _thrust -= _driveKeys[LEFT] * _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right; _thrust += _driveKeys[UP] * _scale * THRUST_MAG_UP * _thrustMultiplier * deltaTime * up; _thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up; + + // attenuate thrust when in penetration + if (glm::dot(_thrust, _lastBodyPenetration) > 0.f) { + const float MAX_BODY_PENETRATION_DEPTH = 0.6f * _skeletonModel.getBoundingShapeRadius(); + float penetrationFactor = glm::min(1.f, glm::length(_lastBodyPenetration) / MAX_BODY_PENETRATION_DEPTH); + glm::vec3 penetrationDirection = glm::normalize(_lastBodyPenetration); + // attenuate parallel component + glm::vec3 parallelThrust = glm::dot(_thrust, penetrationDirection) * penetrationDirection; + // attenuate perpendicular component (friction) + glm::vec3 perpendicularThrust = _thrust - parallelThrust; + // recombine to get the final thrust + _thrust = (1.f - penetrationFactor) * parallelThrust + (1.f - penetrationFactor * penetrationFactor) * perpendicularThrust; + + // attenuate the growth of _thrustMultiplier when in penetration + // otherwise the avatar will eventually be able to tunnel through the obstacle + _thrustMultiplier *= (1.f - penetrationFactor * penetrationFactor); + } else if (_thrustMultiplier < 1.f) { + // rapid healing of attenuated thrustMultiplier after penetration event + _thrustMultiplier = 1.f; + } + _lastBodyPenetration = glm::vec3(0.f); + _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); @@ -686,8 +708,9 @@ void MyAvatar::updateThrust(float deltaTime) { const float THRUST_INCREASE_RATE = 1.05f; const float MAX_THRUST_MULTIPLIER = 75.0f; //printf("m = %.3f\n", _thrustMultiplier); - if (_thrustMultiplier < MAX_THRUST_MULTIPLIER) { - _thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE; + _thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE; + if (_thrustMultiplier > MAX_THRUST_MULTIPLIER) { + _thrustMultiplier = MAX_THRUST_MULTIPLIER; } } else { _thrustMultiplier = 1.f; @@ -917,10 +940,10 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { CollisionInfo* collision = bodyCollisions.getCollision(j); totalPenetration = addPenetrations(totalPenetration, collision->_penetration); } - if (glm::length2(totalPenetration) > EPSILON) { setPosition(getPosition() - BODY_COLLISION_RESOLVE_FACTOR * totalPenetration); } + _lastBodyPenetration += totalPenetration; // collide our hands against them // TODO: make this work when we can figure out when the other avatar won't yeild diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 38edc5356e..5c940f0f50 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -121,6 +121,7 @@ private: bool _isThrustOn; float _thrustMultiplier; glm::vec3 _moveTarget; + glm::vec3 _lastBodyPenetration; int _moveTargetStepCounter; QWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 43eb7fda67..e7c94deb43 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -180,6 +180,7 @@ public: void applyCollision(CollisionInfo& collision); float getBoundingRadius() const { return _boundingRadius; } + float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } /// Sets blended vertices computed in a separate thread. void setBlendedVertices(const QVector& vertices, const QVector& normals); From 97ba5250a5d920fc93770c50ee372a948c279954 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Apr 2014 12:01:18 -0700 Subject: [PATCH 8/9] Only simulate() Avatar Models when necessary Also: rebuild collision shapes when Model scale changes --- interface/src/avatar/Avatar.cpp | 33 ++++++++++++++-------- interface/src/avatar/FaceModel.cpp | 5 ++-- interface/src/avatar/MyAvatar.cpp | 2 ++ interface/src/avatar/SkeletonModel.cpp | 3 ++ interface/src/renderer/Model.cpp | 38 ++++++++++++++++---------- interface/src/renderer/Model.h | 9 +++--- libraries/avatars/src/AvatarData.cpp | 2 ++ libraries/avatars/src/AvatarData.h | 2 ++ 8 files changed, 59 insertions(+), 35 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5e12282f53..ec8539954a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -117,20 +117,29 @@ void Avatar::simulate(float deltaTime) { getHand()->simulate(deltaTime, false); _skeletonModel.setLODDistance(getLODDistance()); - // copy joint data to skeleton - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData.at(i); - _skeletonModel.setJointState(i, data.valid, data.rotation); - } - glm::vec3 headPosition = _position; if (!_shouldRenderBillboard && inViewFrustum) { - _skeletonModel.simulate(deltaTime); - _skeletonModel.getHeadPosition(headPosition); + glm::vec3 headPosition = _position; + + _skeletonModel.updateGeometry(); + if (_skeletonModel.isActive()) { + // copy joint data to skeleton + if (_hasNewJointRotations) { + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData.at(i); + _skeletonModel.setJointState(i, data.valid, data.rotation); + } + _skeletonModel.simulate(deltaTime); + _hasNewJointRotations = false; + } + _skeletonModel.getHeadPosition(headPosition); + } + + Head* head = getHead(); + head->setPosition(headPosition); + head->setScale(_scale); + head->getFaceModel().updateGeometry(); + head->simulate(deltaTime, false, _shouldRenderBillboard); } - Head* head = getHead(); - head->setPosition(headPosition); - head->setScale(_scale); - head->simulate(deltaTime, false, _shouldRenderBillboard); // use speed and angular velocity to determine walking vs. standing if (_speed + fabs(_bodyYawDelta) > 0.2) { diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index c483642d15..5d9b71f11d 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -19,8 +19,7 @@ FaceModel::FaceModel(Head* owningHead) : } void FaceModel::simulate(float deltaTime) { - bool geometryIsUpToDate = updateGeometry(); - if (!geometryIsUpToDate) { + if (!isActive()) { return; } Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); @@ -42,7 +41,7 @@ void FaceModel::simulate(float deltaTime) { setPupilDilation(_owningHead->getPupilDilation()); setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients()); - Model::simulateInternal(deltaTime); + Model::simulate(deltaTime); } void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9dcfaa09ba..0b220a532c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -289,6 +289,7 @@ void MyAvatar::simulate(float deltaTime) { getHand()->collideAgainstOurself(); getHand()->simulate(deltaTime, true); + _skeletonModel.updateGeometry(); _skeletonModel.simulate(deltaTime); // copy out the skeleton joints from the model @@ -305,6 +306,7 @@ void MyAvatar::simulate(float deltaTime) { } head->setPosition(headPosition); head->setScale(_scale); + head->getFaceModel().updateGeometry(); head->simulate(deltaTime, true); // Zero thrust out now that we've added it to velocity in this frame diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index e6318e2003..0ce97f4ef9 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -19,6 +19,9 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) : } void SkeletonModel::simulate(float deltaTime) { + if (!isActive()) { + return; + } setTranslation(_owningAvatar->getPosition()); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f))); const float MODEL_SCALE = 0.0006f; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 1e609afe33..000a41b7d5 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -56,6 +56,14 @@ Model::SkinLocations Model::_skinLocations; Model::SkinLocations Model::_skinNormalMapLocations; Model::SkinLocations Model::_skinShadowLocations; +void Model::setScale(const glm::vec3& scale) { + glm::vec3 deltaScale = _scale - scale; + if (glm::length2(deltaScale) > EPSILON) { + _scale = scale; + rebuildShapes(); + } +} + void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) { program.bind(); locations.clusterMatrices = program.uniformLocation("clusterMatrices"); @@ -182,10 +190,14 @@ void Model::reset() { } } -// return 'true' if geometry is up to date -bool Model::updateGeometry() { - bool needToRebuild = false; +void Model::updateGeometry() { + // NOTE: this is a recursive call that walks all attachments, and their attachments + for (int i = 0; i < _attachments.size(); i++) { + Model* model = _attachments.at(i); + model->updateGeometry(); + } + bool needToRebuild = false; if (_nextGeometry) { _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis); _nextGeometry->setLoadPriority(this, -_lodDistance); @@ -197,7 +209,7 @@ bool Model::updateGeometry() { } if (!_geometry) { // geometry is not ready - return false; + return; } QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); @@ -260,9 +272,9 @@ bool Model::updateGeometry() { model->setURL(attachment.url); _attachments.append(model); } - createShapes(); + rebuildShapes(); } - return true; + return; } bool Model::render(float alpha, bool forShadowMap) { @@ -440,7 +452,7 @@ void Model::clearShapes() { _jointShapes.clear(); } -void Model::createShapes() { +void Model::rebuildShapes() { clearShapes(); if (_jointStates.isEmpty()) { @@ -670,20 +682,16 @@ void Blender::run() { Q_ARG(const QVector&, vertices), Q_ARG(const QVector&, normals)); } - void Model::simulate(float deltaTime) { - bool geometryIsUpToDate = updateGeometry(); - if (!geometryIsUpToDate) { + // NOTE: this is a recursive call that walks all attachments, and their attachments + if (!isActive()) { return; } - simulateInternal(deltaTime); -} - -void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } + _shapesAreDirty = true; const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -720,7 +728,6 @@ void Model::simulateInternal(float deltaTime) { } void Model::updateJointState(int index) { - _shapesAreDirty = true; JointState& state = _jointStates[index]; const FBXGeometry& geometry = _geometry->getFBXGeometry(); const FBXJoint& joint = geometry.joints.at(index); @@ -838,6 +845,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last for (int j = freeLineage.size() - 1; j >= 0; j--) { updateJointState(freeLineage.at(j)); } + _shapesAreDirty = true; return true; } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index e7c94deb43..0c05b18429 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -36,7 +36,7 @@ public: void setRotation(const glm::quat& rotation) { _rotation = rotation; } const glm::quat& getRotation() const { return _rotation; } - void setScale(const glm::vec3& scale) { _scale = scale; } + void setScale(const glm::vec3& scale); const glm::vec3& getScale() const { return _scale; } void setOffset(const glm::vec3& offset) { _offset = offset; } @@ -156,7 +156,7 @@ public: float getRightArmLength() const; void clearShapes(); - void createShapes(); + void rebuildShapes(); void updateShapePositions(); void renderJointCollisionShapes(float alpha); void renderBoundingCollisionShapes(float alpha); @@ -185,6 +185,8 @@ public: /// Sets blended vertices computed in a separate thread. void setBlendedVertices(const QVector& vertices, const QVector& normals); + void updateGeometry(); + protected: QSharedPointer _geometry; @@ -217,9 +219,6 @@ protected: QVector _meshStates; - bool updateGeometry(); - void simulateInternal(float deltaTime); - /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 31639b6836..930e3f7350 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -40,6 +40,7 @@ AvatarData::AvatarData() : _handState(0), _keyState(NO_KEY_DOWN), _isChatCirclingEnabled(false), + _hasNewJointRotations(true), _headData(NULL), _handData(NULL), _displayNameBoundingRect(), @@ -483,6 +484,7 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { } } } // numJoints * 8 bytes + _hasNewJointRotations = true; return sourceBuffer - startPosition; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 2ea20c1041..221bbd0428 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -242,6 +242,8 @@ protected: bool _isChatCirclingEnabled; + bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar + HeadData* _headData; HandData* _handData; From 933cade6b0391a8975e0a7367ba8da247ceba029 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Apr 2014 16:34:33 -0700 Subject: [PATCH 9/9] Re-add fullUpdate hint to Model::simulate() --- interface/src/avatar/Avatar.cpp | 23 +++++++++------------ interface/src/avatar/FaceModel.cpp | 10 ++++----- interface/src/avatar/MyAvatar.cpp | 2 -- interface/src/avatar/SkeletonModel.cpp | 7 ++----- interface/src/avatar/SkeletonModel.h | 2 +- interface/src/renderer/Model.cpp | 28 +++++++++++++++++--------- interface/src/renderer/Model.h | 9 ++++++--- 7 files changed, 42 insertions(+), 39 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ec8539954a..899514d1c1 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -118,26 +118,21 @@ void Avatar::simulate(float deltaTime) { _skeletonModel.setLODDistance(getLODDistance()); if (!_shouldRenderBillboard && inViewFrustum) { - glm::vec3 headPosition = _position; - - _skeletonModel.updateGeometry(); - if (_skeletonModel.isActive()) { - // copy joint data to skeleton - if (_hasNewJointRotations) { - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData.at(i); - _skeletonModel.setJointState(i, data.valid, data.rotation); - } - _skeletonModel.simulate(deltaTime); - _hasNewJointRotations = false; + if (_hasNewJointRotations) { + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData.at(i); + _skeletonModel.setJointState(i, data.valid, data.rotation); } - _skeletonModel.getHeadPosition(headPosition); + _skeletonModel.simulate(deltaTime); } + _skeletonModel.simulate(deltaTime, _hasNewJointRotations); + _hasNewJointRotations = false; + glm::vec3 headPosition = _position; + _skeletonModel.getHeadPosition(headPosition); Head* head = getHead(); head->setPosition(headPosition); head->setScale(_scale); - head->getFaceModel().updateGeometry(); head->simulate(deltaTime, false, _shouldRenderBillboard); } diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 5d9b71f11d..2934a022af 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -19,9 +19,7 @@ FaceModel::FaceModel(Head* owningHead) : } void FaceModel::simulate(float deltaTime) { - if (!isActive()) { - return; - } + updateGeometry(); Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); glm::vec3 neckPosition; if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { @@ -36,12 +34,14 @@ void FaceModel::simulate(float deltaTime) { const float MODEL_SCALE = 0.0006f; setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE); - setOffset(-_geometry->getFBXGeometry().neckPivot); + if (isActive()) { + setOffset(-_geometry->getFBXGeometry().neckPivot); + } setPupilDilation(_owningHead->getPupilDilation()); setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients()); - Model::simulate(deltaTime); + Model::simulateInternal(deltaTime); } void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0b220a532c..9dcfaa09ba 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -289,7 +289,6 @@ void MyAvatar::simulate(float deltaTime) { getHand()->collideAgainstOurself(); getHand()->simulate(deltaTime, true); - _skeletonModel.updateGeometry(); _skeletonModel.simulate(deltaTime); // copy out the skeleton joints from the model @@ -306,7 +305,6 @@ void MyAvatar::simulate(float deltaTime) { } head->setPosition(headPosition); head->setScale(_scale); - head->getFaceModel().updateGeometry(); head->simulate(deltaTime, true); // Zero thrust out now that we've added it to velocity in this frame diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0ce97f4ef9..69229d9e8d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -18,16 +18,13 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) : _owningAvatar(owningAvatar) { } -void SkeletonModel::simulate(float deltaTime) { - if (!isActive()) { - return; - } +void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setTranslation(_owningAvatar->getPosition()); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f))); const float MODEL_SCALE = 0.0006f; setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE); - Model::simulate(deltaTime); + Model::simulate(deltaTime, fullUpdate); if (!(isActive() && _owningAvatar->isMyAvatar())) { return; // only simulate for own avatar diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 60b12eb8f5..31867dec5c 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -21,7 +21,7 @@ public: SkeletonModel(Avatar* owningAvatar); - void simulate(float deltaTime); + void simulate(float deltaTime, bool fullUpdate = true); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 000a41b7d5..9d2a031a6e 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -190,11 +190,14 @@ void Model::reset() { } } -void Model::updateGeometry() { +bool Model::updateGeometry() { // NOTE: this is a recursive call that walks all attachments, and their attachments + bool needFullUpdate = false; for (int i = 0; i < _attachments.size(); i++) { Model* model = _attachments.at(i); - model->updateGeometry(); + if (model->updateGeometry()) { + needFullUpdate = true; + } } bool needToRebuild = false; @@ -209,7 +212,7 @@ void Model::updateGeometry() { } if (!_geometry) { // geometry is not ready - return; + return false; } QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); @@ -273,8 +276,9 @@ void Model::updateGeometry() { _attachments.append(model); } rebuildShapes(); + needFullUpdate = true; } - return; + return needFullUpdate; } bool Model::render(float alpha, bool forShadowMap) { @@ -682,11 +686,15 @@ void Blender::run() { Q_ARG(const QVector&, vertices), Q_ARG(const QVector&, normals)); } -void Model::simulate(float deltaTime) { - // NOTE: this is a recursive call that walks all attachments, and their attachments - if (!isActive()) { - return; +void Model::simulate(float deltaTime, bool fullUpdate) { + fullUpdate = updateGeometry() || fullUpdate; + if (isActive() && fullUpdate) { + simulateInternal(deltaTime); } +} + +void Model::simulateInternal(float deltaTime) { + // NOTE: this is a recursive call that walks all attachments, and their attachments // update the world space transforms for all joints for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); @@ -709,7 +717,9 @@ void Model::simulate(float deltaTime) { model->setRotation(jointRotation * attachment.rotation); model->setScale(_scale * attachment.scale); - model->simulate(deltaTime); + if (model->isActive()) { + model->simulateInternal(deltaTime); + } } for (int i = 0; i < _meshStates.size(); i++) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 0c05b18429..11374a6369 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -56,7 +56,7 @@ public: void init(); void reset(); - virtual void simulate(float deltaTime); + virtual void simulate(float deltaTime, bool fullUpdate = true); bool render(float alpha = 1.0f, bool forShadowMap = false); /// Sets the URL of the model to render. @@ -185,8 +185,6 @@ public: /// Sets blended vertices computed in a separate thread. void setBlendedVertices(const QVector& vertices, const QVector& normals); - void updateGeometry(); - protected: QSharedPointer _geometry; @@ -219,6 +217,11 @@ protected: QVector _meshStates; + // returns 'true' if needs fullUpdate after geometry change + bool updateGeometry(); + + void simulateInternal(float deltaTime); + /// Updates the state of the joint at the specified index. virtual void updateJointState(int index);