Merge pull request #4465 from sethalves/avatars-can-collide

One convex hull shape for models that provide decomposition data.
This commit is contained in:
Andrew Meadows 2015-03-18 12:52:52 -07:00
commit 517b2648ca
18 changed files with 221 additions and 59 deletions

View file

@ -266,4 +266,53 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking);
}
bool RenderableModelEntityItem::isReadyToComputeShape() {
if (_collisionModelURL == "") {
// no model url, so we're ready to compute a shape.
return true;
}
if (! _collisionNetworkGeometry.isNull() && _collisionNetworkGeometry->isLoadedWithTextures()) {
// we have a _collisionModelURL AND a _collisionNetworkGeometry AND it's fully loaded.
return true;
}
if (_collisionNetworkGeometry.isNull()) {
// we have a _collisionModelURL but we don't yet have a _collisionNetworkGeometry.
_collisionNetworkGeometry =
DependencyManager::get<GeometryCache>()->getGeometry(_collisionModelURL, QUrl(), false, false);
if (! _collisionNetworkGeometry.isNull() && _collisionNetworkGeometry->isLoadedWithTextures()) {
// shortcut in case it's already loaded.
return true;
}
}
// the model is still being downloaded.
return false;
}
void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
if (_collisionModelURL == "") {
info.setParams(getShapeType(), 0.5f * getDimensions());
} else {
const FBXGeometry& fbxGeometry = _collisionNetworkGeometry->getFBXGeometry();
_points.clear();
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
_points << mesh.vertices;
}
info.setParams(getShapeType(), 0.5f * getDimensions(), NULL, _collisionModelURL);
info.setConvexHull(_points);
}
}
ShapeType RenderableModelEntityItem::getShapeType() const {
// XXX make hull an option in edit.js ?
if (_collisionModelURL != "") {
return SHAPE_TYPE_CONVEX_HULL;
} else {
return _shapeType;
}
}

View file

@ -30,7 +30,8 @@ public:
_needsInitialSimulation(true),
_needsModelReload(true),
_myRenderer(NULL),
_originalTexturesRead(false) { }
_originalTexturesRead(false),
_collisionNetworkGeometry(QSharedPointer<NetworkGeometry>()) { }
virtual ~RenderableModelEntityItem();
@ -52,6 +53,10 @@ public:
bool needsToCallUpdate() const;
bool isReadyToComputeShape();
void computeShapeInfo(ShapeInfo& info);
ShapeType getShapeType() const;
private:
void remapTextures();
@ -62,6 +67,9 @@ private:
QString _currentTextures;
QStringList _originalTextures;
bool _originalTexturesRead;
QSharedPointer<NetworkGeometry> _collisionNetworkGeometry;
QVector<glm::vec3> _points;
};
#endif // hifi_RenderableModelEntityItem_h

View file

@ -1002,7 +1002,7 @@ float EntityItem::getRadius() const {
return 0.5f * glm::length(_dimensions);
}
void EntityItem::computeShapeInfo(ShapeInfo& info) const {
void EntityItem::computeShapeInfo(ShapeInfo& info) {
info.setParams(getShapeType(), 0.5f * getDimensions());
}

View file

@ -43,7 +43,7 @@ class EntityTreeElementExtraEncodeData;
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
/// one directly, instead you must only construct one of it's derived classes with additional features.
class EntityItem {
class EntityItem {
friend class EntityTreeElement;
public:
enum EntityDirtyFlags {
@ -256,7 +256,9 @@ public:
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
virtual bool containsInDomainUnits(const glm::vec3& point) const { return getAABoxInDomainUnits().contains(point); }
virtual void computeShapeInfo(ShapeInfo& info) const;
virtual bool isReadyToComputeShape() { return true; }
virtual void computeShapeInfo(ShapeInfo& info);
/// return preferred shape type (actual physical shape may differ)
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
@ -295,7 +297,6 @@ public:
static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; }
static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; }
protected:
static bool _sendPhysicsUpdates;

View file

@ -176,7 +176,8 @@ void EntityItemProperties::setLastEdited(quint64 usecTime) {
_lastEdited = usecTime > _created ? usecTime : _created;
}
const char* shapeTypeNames[] = {"none", "box", "sphere"};
const char* shapeTypeNames[] = {"none", "box", "sphere", "ellipsoid", "convex-hull", "plane", "compound", "capsule-x",
"capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"};
QHash<QString, ShapeType> stringToShapeTypeLookup;
@ -184,6 +185,16 @@ void buildStringToShapeTypeLookup() {
stringToShapeTypeLookup["none"] = SHAPE_TYPE_NONE;
stringToShapeTypeLookup["box"] = SHAPE_TYPE_BOX;
stringToShapeTypeLookup["sphere"] = SHAPE_TYPE_SPHERE;
stringToShapeTypeLookup["ellipsoid"] = SHAPE_TYPE_ELLIPSOID;
stringToShapeTypeLookup["convex-hull"] = SHAPE_TYPE_CONVEX_HULL;
stringToShapeTypeLookup["plane"] = SHAPE_TYPE_PLANE;
stringToShapeTypeLookup["compound"] = SHAPE_TYPE_COMPOUND;
stringToShapeTypeLookup["capsule-x"] = SHAPE_TYPE_CAPSULE_X;
stringToShapeTypeLookup["capsule-y"] = SHAPE_TYPE_CAPSULE_Y;
stringToShapeTypeLookup["capsule-z"] = SHAPE_TYPE_CAPSULE_Z;
stringToShapeTypeLookup["cylinder-x"] = SHAPE_TYPE_CYLINDER_X;
stringToShapeTypeLookup["cylinder-y"] = SHAPE_TYPE_CYLINDER_Y;
stringToShapeTypeLookup["cylinder-z"] = SHAPE_TYPE_CYLINDER_Z;
}
QString EntityItemProperties::getShapeTypeAsString() const {

View file

@ -17,6 +17,7 @@
#include "EntityTree.h"
#include "EntityTreeElement.h"
#include "ModelEntityItem.h"
#include "ResourceCache.h"
const QString ModelEntityItem::DEFAULT_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString("");

View file

@ -48,14 +48,26 @@ void ResourceCache::refresh(const QUrl& url) {
}
}
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) {
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback,
bool delayLoad, void* extra, bool block) {
if (QThread::currentThread() != thread()) {
QSharedPointer<Resource> result;
QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QSharedPointer<Resource>, result), Q_ARG(const QUrl&, url), Q_ARG(const QUrl&, fallback),
Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
return result;
// This will re-call this method in the main thread. If block is true and the main thread
// is waiting on a lock, we'll deadlock here.
if (block) {
QSharedPointer<Resource> result;
QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QSharedPointer<Resource>, result), Q_ARG(const QUrl&, url),
Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
return result;
} else {
// Queue the re-invocation of this method, but if the main thread is blocked, don't wait. The
// return value may be NULL -- it's expected that this will be called again later, in order
// to receive the actual Resource.
QMetaObject::invokeMethod(this, "getResource", Qt::QueuedConnection,
Q_ARG(const QUrl&, url),
Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
return _resources.value(url);
}
}
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {

View file

@ -89,7 +89,7 @@ protected:
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
/// \param extra extra data to pass to the creator, if appropriate
Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
bool delayLoad = false, void* extra = NULL);
bool delayLoad = false, void* extra = NULL, bool block = true);
/// Creates a new resource.
virtual QSharedPointer<Resource> createResource(const QUrl& url,

View file

@ -63,23 +63,26 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (!physicsInfo) {
ShapeInfo shapeInfo;
entity->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) {
EntityMotionState* motionState = new EntityMotionState(entity);
entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState);
addObject(shapeInfo, shape, motionState);
} else if (entity->isMoving()) {
EntityMotionState* motionState = new EntityMotionState(entity);
entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState);
qDebug() << "PhysicsEngine::addEntityInternal(" << entity;
if (entity->isReadyToComputeShape()) {
ShapeInfo shapeInfo;
entity->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) {
EntityMotionState* motionState = new EntityMotionState(entity);
entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState);
addObject(shapeInfo, shape, motionState);
} else if (entity->isMoving()) {
EntityMotionState* motionState = new EntityMotionState(entity);
entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState);
motionState->setKinematic(true, _numSubsteps);
_nonPhysicalKinematicObjects.insert(motionState);
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
motionState->setKinematic(true, _numSubsteps);
_nonPhysicalKinematicObjects.insert(motionState);
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
}
}
}
}
@ -322,16 +325,21 @@ void PhysicsEngine::stepSimulation() {
//
// TODO: untangle these lock sequences.
_entityTree->lockForWrite();
_avatarData->lockForWrite();
lock();
_dynamicsWorld->synchronizeMotionStates();
if (_avatarData->isPhysicsEnabled()) {
_avatarData->lockForRead();
bool avatarHasPhysicsEnabled = _avatarData->isPhysicsEnabled();
_avatarData->unlock();
if (avatarHasPhysicsEnabled) {
const btTransform& avatarTransform = _avatarGhostObject->getWorldTransform();
glm::quat rotation = bulletToGLM(avatarTransform.getRotation());
glm::vec3 offset = rotation * _avatarShapeLocalOffset;
_avatarData->lockForWrite();
_avatarData->setOrientation(rotation);
_avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset);
_avatarData->unlock();
}
unlock();

View file

@ -26,6 +26,9 @@ int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
case SHAPE_TYPE_CAPSULE_Y:
bulletShapeType = CAPSULE_SHAPE_PROXYTYPE;
break;
case SHAPE_TYPE_CONVEX_HULL:
bulletShapeType = CONVEX_HULL_SHAPE_PROXYTYPE;
break;
}
return bulletShapeType;
}
@ -42,6 +45,9 @@ int ShapeInfoUtil::fromBulletShapeType(int bulletShapeType) {
case CAPSULE_SHAPE_PROXYTYPE:
shapeInfoType = SHAPE_TYPE_CAPSULE_Y;
break;
case CONVEX_HULL_SHAPE_PROXYTYPE:
shapeInfoType = SHAPE_TYPE_CONVEX_HULL;
break;
}
return shapeInfoType;
}
@ -60,8 +66,21 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf
info.setSphere(sphereShape->getRadius());
}
break;
default:
case SHAPE_TYPE_CONVEX_HULL: {
const btConvexHullShape* convexHullShape = static_cast<const btConvexHullShape*>(shape);
const int numPoints = convexHullShape->getNumPoints();
const btVector3* btPoints = convexHullShape->getUnscaledPoints();
QVector<glm::vec3> points;
for (int i = 0; i < numPoints; i++) {
glm::vec3 point(btPoints->getX(), btPoints->getY(), btPoints->getZ());
points << point;
}
info.setConvexHull(points);
}
break;
default: {
info.clear();
}
break;
}
} else {
@ -88,6 +107,15 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
shape = new btCapsuleShape(radius, height);
}
break;
case SHAPE_TYPE_CONVEX_HULL: {
shape = new btConvexHullShape();
const QVector<glm::vec3>& points = info.getPoints();
foreach (glm::vec3 point, points) {
btVector3 btPoint(point[0], point[1], point[2]);
static_cast<btConvexHullShape*>(shape)->addPoint(btPoint);
}
}
break;
}
return shape;
}

View file

@ -1770,8 +1770,8 @@ void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2,
}
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) {
return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>();
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad, bool block) {
return getResource(url, fallback, delayLoad, NULL, block).staticCast<NetworkGeometry>();
}
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,

View file

@ -21,8 +21,8 @@
#include <DependencyManager.h>
#include <ResourceCache.h>
#include <FBXReader.h>
#include <OBJReader.h>
#include "FBXReader.h"
#include "OBJReader.h"
#include <AnimationCache.h>
@ -203,7 +203,8 @@ public:
/// Loads geometry from the specified URL.
/// \param fallback a fallback URL to load if the desired one is unavailable
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(),
bool delayLoad = false, bool block = true);
protected:

View file

@ -24,7 +24,7 @@
#include <gpu/GLBackend.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <PhysicsEntity.h>
#include "PhysicsEntity.h"
#include <ShapeCollider.h>
#include <SphereShape.h>
#include <ViewFrustum.h>
@ -1101,6 +1101,14 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
}
}
void Model::setCollisionModelURL(const QUrl& url, const QUrl& fallback, bool delayLoad) {
if (_collisionUrl == url) {
return;
}
_collisionUrl = url;
_collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(url, fallback, delayLoad);
}
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;

View file

@ -24,7 +24,7 @@
#include <GeometryUtil.h>
#include <gpu/Stream.h>
#include <gpu/Batch.h>
#include <PhysicsEntity.h>
#include "PhysicsEntity.h"
#include <Transform.h>
#include "AnimationHandle.h"
@ -106,7 +106,10 @@ public:
/// \param delayLoad if true, don't load the model immediately; wait until actually requested
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(),
bool retainCurrent = false, bool delayLoad = false);
// Set the model to use for collisions
Q_INVOKABLE void setCollisionModelURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
const QUrl& getURL() const { return _url; }
/// Sets the distance parameter used for LOD computations.
@ -287,11 +290,14 @@ private:
float _lodDistance;
float _lodHysteresis;
float _nextLODHysteresis;
QSharedPointer<NetworkGeometry> _collisionGeometry;
float _pupilDilation;
QVector<float> _blendshapeCoefficients;
QUrl _url;
QUrl _collisionUrl;
gpu::Buffers _blendedVertexBuffers;
std::vector<Transform> _transforms;

View file

@ -22,7 +22,7 @@ void ShapeInfo::clear() {
_externalData = NULL;
}
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data) {
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data, QString url) {
_type = type;
switch(type) {
case SHAPE_TYPE_NONE:
@ -37,8 +37,13 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<
_halfExtents = glm::vec3(radius);
break;
}
case SHAPE_TYPE_CONVEX_HULL:
_url = QUrl(url);
_halfExtents = halfExtents;
break;
default:
_halfExtents = halfExtents;
break;
}
_externalData = data;
}
@ -61,6 +66,11 @@ void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) {
_doubleHashKey.clear();
}
void ShapeInfo::setConvexHull(const QVector<glm::vec3>& points) {
_type = SHAPE_TYPE_CONVEX_HULL;
_points = points;
}
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
_type = SHAPE_TYPE_CAPSULE_Y;
_halfExtents = glm::vec3(radius, halfHeight, radius);
@ -130,21 +140,30 @@ const DoubleHashKey& ShapeInfo::getHash() const {
thisPtr->_doubleHashKey.setHash(hash);
// compute hash2
hash = _doubleHashKey.getHash2();
for (int i = 0; i < numData; ++i) {
tmpData = (*data)[i];
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction2((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
QString url = _url.toString();
if (url == "") {
hash = _doubleHashKey.getHash2();
for (int i = 0; i < numData; ++i) {
tmpData = (*data)[i];
for (int j = 0; j < 3; ++j) {
// NOTE: 0.49f is used to bump the float up almost half a millimeter
// so the cast to int produces a round() effect rather than a floor()
uint32_t floatHash =
DoubleHashKey::hashFunction2((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
}
} else {
QByteArray baUrl = url.toLocal8Bit();
const char *cUrl = baUrl.data();
hash = qChecksum(cUrl, baUrl.count());
}
thisPtr->_doubleHashKey.setHash2(hash);
} else {

View file

@ -13,6 +13,8 @@
#define hifi_ShapeInfo_h
#include <QVector>
#include <QString>
#include <QUrl>
#include <glm/glm.hpp>
#include "DoubleHashKey.h"
@ -22,7 +24,7 @@ enum ShapeType {
SHAPE_TYPE_BOX,
SHAPE_TYPE_SPHERE,
SHAPE_TYPE_ELLIPSOID,
SHAPE_TYPE_HULL,
SHAPE_TYPE_CONVEX_HULL,
SHAPE_TYPE_PLANE,
SHAPE_TYPE_COMPOUND,
SHAPE_TYPE_CAPSULE_X,
@ -34,14 +36,15 @@ enum ShapeType {
};
class ShapeInfo {
public:
void clear();
void setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data = NULL);
void setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data = NULL, QString url="");
void setBox(const glm::vec3& halfExtents);
void setSphere(float radius);
void setEllipsoid(const glm::vec3& halfExtents);
//void setHull(); // TODO: implement this
void setConvexHull(const QVector<glm::vec3>& points);
void setCapsuleY(float radius, float halfHeight);
const int getType() const { return _type; }
@ -51,6 +54,11 @@ public:
void setData(const QVector<glm::vec3>* data) { _externalData = data; }
const QVector<glm::vec3>* getData() const { return _externalData; }
const QVector<glm::vec3>& getPoints() const { return _points; }
void clearPoints () { _points.clear(); }
void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
float computeVolume() const;
const DoubleHashKey& getHash() const;
@ -60,6 +68,8 @@ protected:
glm::vec3 _halfExtents = glm::vec3(0.0f);
DoubleHashKey _doubleHashKey;
const QVector<glm::vec3>* _externalData = NULL;
QVector<glm::vec3> _points; // points for convex collision hull
QUrl _url; // url for model of convex collision hull
};
#endif // hifi_ShapeInfo_h