mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
add SHAPE_TYPE_MESH and build mesh shapes
This commit is contained in:
parent
9fc77ccfa2
commit
a519b77ae7
6 changed files with 235 additions and 32 deletions
|
@ -599,13 +599,13 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
|
||||
void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||
ShapeType type = getShapeType();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
updateModelBounds();
|
||||
|
||||
// should never fall in here when collision model not fully loaded
|
||||
// hence we assert that all geometries exist and are loaded
|
||||
assert(_model->isLoaded() && _model->isCollisionLoaded());
|
||||
const FBXGeometry& renderGeometry = _model->getFBXGeometry();
|
||||
const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry();
|
||||
|
||||
ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||
|
@ -677,7 +677,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
// to the visual model and apply them to the collision model (without regard for the
|
||||
// collision model's extents).
|
||||
|
||||
glm::vec3 scale = getDimensions() / renderGeometry.getUnscaledMeshExtents().size();
|
||||
const FBXGeometry& renderGeometry = _model->getFBXGeometry();
|
||||
glm::vec3 scale = dimensions / renderGeometry.getUnscaledMeshExtents().size();
|
||||
// multiply each point by scale before handing the point-set off to the physics engine.
|
||||
// also determine the extents of the collision model.
|
||||
AABox box;
|
||||
|
@ -697,9 +698,104 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
glm::vec3 collisionModelDimensions = box.getDimensions();
|
||||
info.setParams(type, collisionModelDimensions, _compoundShapeURL);
|
||||
info.setOffset(_model->getOffset());
|
||||
} else if (type == SHAPE_TYPE_MESH) {
|
||||
updateModelBounds();
|
||||
|
||||
// should never fall in here when collision model not fully loaded
|
||||
assert(_model->isLoaded());
|
||||
|
||||
ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||
pointCollection.clear();
|
||||
|
||||
ShapeInfo::PointList points;
|
||||
ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
|
||||
auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
|
||||
|
||||
glm::vec3 modelOffset = _model->getOffset();
|
||||
for (auto& mesh : meshes) {
|
||||
const gpu::BufferView& vertices = mesh->getVertexBuffer();
|
||||
const gpu::BufferView& indices = mesh->getIndexBuffer();
|
||||
const gpu::BufferView& parts = mesh->getPartBuffer();
|
||||
|
||||
// copy points
|
||||
uint32_t meshIndexOffset = (uint32_t)points.size();
|
||||
gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertices.cbegin<const glm::vec3>();
|
||||
points.reserve(points.size() + vertices.getNumElements());
|
||||
Extents extents;
|
||||
while (vertexItr != vertices.cend<const glm::vec3>()) {
|
||||
points.push_back(*vertexItr);
|
||||
extents.addPoint(*vertexItr);
|
||||
++vertexItr;
|
||||
}
|
||||
|
||||
// scale points and shift by modelOffset
|
||||
glm::vec3 extentsSize = extents.size();
|
||||
glm::vec3 scale = dimensions / extents.size();
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (extentsSize[i] < 1.0e-6f) {
|
||||
scale[i] = 1.0f;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < points.size(); ++i) {
|
||||
points[i] = (points[i] * scale) + modelOffset;
|
||||
}
|
||||
|
||||
// copy triangleIndices
|
||||
triangleIndices.reserve(triangleIndices.size() + indices.getNumElements());
|
||||
gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
|
||||
while (partItr != parts.cend<const model::Mesh::Part>()) {
|
||||
|
||||
if (partItr->_topology == model::Mesh::TRIANGLES) {
|
||||
assert(partItr->_numIndices % 3 == 0);
|
||||
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||
auto indexEnd = indexItr + partItr->_numIndices;
|
||||
while (indexItr != indexEnd) {
|
||||
triangleIndices.push_back(*indexItr + meshIndexOffset);
|
||||
++indexItr;
|
||||
}
|
||||
} else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) {
|
||||
assert(partItr->_numIndices > 2);
|
||||
uint32_t approxNumIndices = 3 * partItr->_numIndices;
|
||||
if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) {
|
||||
// we underestimated the final size of triangleIndices so we pre-emptively expand it
|
||||
triangleIndices.reserve(triangleIndices.size() + approxNumIndices);
|
||||
}
|
||||
|
||||
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||
auto indexEnd = indexItr + (partItr->_numIndices - 2);
|
||||
|
||||
// first triangle uses the first three indices
|
||||
triangleIndices.push_back(*indexItr + meshIndexOffset);
|
||||
triangleIndices.push_back(*(++indexItr) + meshIndexOffset);
|
||||
triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
|
||||
|
||||
// the rest use previous and next index
|
||||
uint32_t triangleCount = 1;
|
||||
while (indexItr != indexEnd) {
|
||||
if (triangleCount % 2 == 0) {
|
||||
// even triangles use first two indices in order
|
||||
triangleIndices.push_back(*(indexItr) + meshIndexOffset);
|
||||
triangleIndices.push_back(*(++indexItr) + meshIndexOffset); // yes pre-increment
|
||||
} else {
|
||||
// odd triangles swap order of first two indices
|
||||
triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
|
||||
triangleIndices.push_back(*(indexItr++) + meshIndexOffset); // yes post-increment
|
||||
}
|
||||
triangleIndices.push_back(*(indexItr + 1) + meshIndexOffset);
|
||||
++triangleCount;
|
||||
}
|
||||
} else if (partItr->_topology == model::Mesh::QUADS) {
|
||||
// TODO: support model::Mesh::QUADS
|
||||
}
|
||||
// TODO? support model::Mesh::QUAD_STRIP?
|
||||
++partItr;
|
||||
}
|
||||
}
|
||||
pointCollection.push_back(points);
|
||||
info.setParams(SHAPE_TYPE_MESH, 0.5f * dimensions, _modelURL);
|
||||
} else {
|
||||
ModelEntityItem::computeShapeInfo(info);
|
||||
info.setParams(type, 0.5f * getDimensions());
|
||||
info.setParams(type, 0.5f * dimensions);
|
||||
adjustShapeInfoByRegistration(info);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,8 @@ static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = {
|
|||
};
|
||||
|
||||
|
||||
btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& points) {
|
||||
// util method
|
||||
btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) {
|
||||
assert(points.size() > 0);
|
||||
|
||||
btConvexHullShape* hull = new btConvexHullShape();
|
||||
|
@ -158,6 +159,84 @@ btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& poin
|
|||
return hull;
|
||||
}
|
||||
|
||||
// util method
|
||||
btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) {
|
||||
assert(info.getType() == SHAPE_TYPE_MESH); // should only get here for mesh shapes
|
||||
|
||||
const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||
assert(pointCollection.size() == 1); // should only have one mesh
|
||||
|
||||
const ShapeInfo::PointList& pointList = pointCollection[0];
|
||||
assert(pointList.size() > 2); // should have at least one triangle's worth of points
|
||||
|
||||
const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
|
||||
assert(triangleIndices.size() > 2); // should have at least one triangle's worth of indices
|
||||
|
||||
// allocate mesh buffers
|
||||
btIndexedMesh mesh;
|
||||
int32_t numIndices = triangleIndices.size();
|
||||
const int32_t VERTICES_PER_TRIANGLE = 3;
|
||||
mesh.m_numTriangles = numIndices / VERTICES_PER_TRIANGLE;
|
||||
if (numIndices < INT16_MAX) {
|
||||
// small number of points so we can use 16-bit indices
|
||||
mesh.m_triangleIndexBase = new unsigned char[sizeof(int16_t) * (size_t)numIndices];
|
||||
mesh.m_indexType = PHY_SHORT;
|
||||
mesh.m_triangleIndexStride = VERTICES_PER_TRIANGLE * sizeof(int16_t);
|
||||
} else {
|
||||
mesh.m_triangleIndexBase = new unsigned char[sizeof(int32_t) * (size_t)numIndices];
|
||||
mesh.m_indexType = PHY_INTEGER;
|
||||
mesh.m_triangleIndexStride = VERTICES_PER_TRIANGLE * sizeof(int32_t);
|
||||
}
|
||||
mesh.m_numVertices = pointList.size();
|
||||
mesh.m_vertexBase = new unsigned char[VERTICES_PER_TRIANGLE * sizeof(btScalar) * (size_t)mesh.m_numVertices];
|
||||
mesh.m_vertexStride = VERTICES_PER_TRIANGLE * sizeof(btScalar);
|
||||
mesh.m_vertexType = PHY_FLOAT;
|
||||
|
||||
// copy data into buffers
|
||||
btScalar* vertexData = static_cast<btScalar*>((void*)(mesh.m_vertexBase));
|
||||
for (int32_t i = 0; i < mesh.m_numVertices; ++i) {
|
||||
int32_t j = i * VERTICES_PER_TRIANGLE;
|
||||
const glm::vec3& point = pointList[i];
|
||||
vertexData[j] = point.x;
|
||||
vertexData[j + 1] = point.y;
|
||||
vertexData[j + 2] = point.z;
|
||||
}
|
||||
if (numIndices < INT16_MAX) {
|
||||
int16_t* indices = static_cast<int16_t*>((void*)(mesh.m_triangleIndexBase));
|
||||
for (int32_t i = 0; i < numIndices; ++i) {
|
||||
indices[i] = triangleIndices[i];
|
||||
}
|
||||
} else {
|
||||
int32_t* indices = static_cast<int32_t*>((void*)(mesh.m_triangleIndexBase));
|
||||
for (int32_t i = 0; i < numIndices; ++i) {
|
||||
indices[i] = triangleIndices[i];
|
||||
}
|
||||
}
|
||||
|
||||
// store buffers in a new dataArray and return the pointer
|
||||
// (external StaticMeshShape will own all of the data that was allocated here)
|
||||
btTriangleIndexVertexArray* dataArray = new btTriangleIndexVertexArray;
|
||||
dataArray->addIndexedMesh(mesh, mesh.m_indexType);
|
||||
return dataArray;
|
||||
}
|
||||
|
||||
// util method
|
||||
void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) {
|
||||
assert(dataArray);
|
||||
IndexedMeshArray& meshes = dataArray->getIndexedMeshArray();
|
||||
for (int32_t i = 0; i < meshes.size(); ++i) {
|
||||
btIndexedMesh mesh = meshes[i];
|
||||
mesh.m_numTriangles = 0;
|
||||
delete [] mesh.m_triangleIndexBase;
|
||||
mesh.m_triangleIndexBase = nullptr;
|
||||
mesh.m_numVertices = 0;
|
||||
delete [] mesh.m_vertexBase;
|
||||
mesh.m_vertexBase = nullptr;
|
||||
}
|
||||
meshes.clear();
|
||||
delete dataArray;
|
||||
}
|
||||
|
||||
btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||
btCollisionShape* shape = NULL;
|
||||
int type = info.getType();
|
||||
|
@ -195,6 +274,11 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case SHAPE_TYPE_MESH: {
|
||||
btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info);
|
||||
shape = new StaticMeshShape(dataArray);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (shape) {
|
||||
if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
|
||||
|
@ -228,3 +312,14 @@ void ShapeFactory::deleteShape(btCollisionShape* shape) {
|
|||
}
|
||||
delete shape;
|
||||
}
|
||||
|
||||
// the dataArray must be created before we create the StaticMeshShape
|
||||
ShapeFactory::StaticMeshShape::StaticMeshShape(btTriangleIndexVertexArray* dataArray)
|
||||
: btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) {
|
||||
assert(dataArray);
|
||||
}
|
||||
|
||||
ShapeFactory::StaticMeshShape::~StaticMeshShape() {
|
||||
deleteStaticMeshArray(_dataArray);
|
||||
_dataArray = nullptr;
|
||||
}
|
||||
|
|
|
@ -20,9 +20,22 @@
|
|||
// translates between ShapeInfo and btShape
|
||||
|
||||
namespace ShapeFactory {
|
||||
btConvexHullShape* createConvexHull(const QVector<glm::vec3>& points);
|
||||
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||
void deleteShape(btCollisionShape* shape);
|
||||
|
||||
//btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info);
|
||||
//void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray);
|
||||
|
||||
class StaticMeshShape : public btBvhTriangleMeshShape {
|
||||
public:
|
||||
StaticMeshShape() = delete;
|
||||
StaticMeshShape(btTriangleIndexVertexArray* dataArray);
|
||||
~StaticMeshShape();
|
||||
|
||||
private:
|
||||
// the StaticMeshShape owns its vertex/index data
|
||||
btTriangleIndexVertexArray* _dataArray;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // hifi_ShapeFactory_h
|
||||
|
|
|
@ -32,15 +32,13 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||
return NULL;
|
||||
}
|
||||
if (info.getType() != SHAPE_TYPE_COMPOUND) {
|
||||
// Very small or large non-compound objects are not supported.
|
||||
float diagonal = 4.0f * glm::length2(info.getHalfExtents());
|
||||
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
||||
if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED) {
|
||||
// qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
||||
return NULL;
|
||||
}
|
||||
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
||||
if (4.0f * glm::length2(info.getHalfExtents()) < MIN_SHAPE_DIAGONAL_SQUARED) {
|
||||
// tiny shapes are not supported
|
||||
// qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DoubleHashKey key = info.getHash();
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
|
@ -66,8 +64,8 @@ bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) {
|
|||
shapeRef->refCount--;
|
||||
if (shapeRef->refCount == 0) {
|
||||
_pendingGarbage.push_back(key);
|
||||
const int MAX_GARBAGE_CAPACITY = 255;
|
||||
if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
|
||||
const int MAX_SHAPE_GARBAGE_CAPACITY = 255;
|
||||
if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) {
|
||||
collectGarbage();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
|||
break;
|
||||
}
|
||||
case SHAPE_TYPE_COMPOUND:
|
||||
case SHAPE_TYPE_MESH:
|
||||
_url = QUrl(url);
|
||||
_halfExtents = halfExtents;
|
||||
break;
|
||||
// yes, fall through
|
||||
default:
|
||||
_halfExtents = halfExtents;
|
||||
break;
|
||||
|
@ -182,18 +182,15 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
|||
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
|
||||
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
|
||||
bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET;
|
||||
// The key is not yet cached therefore we must compute it! To this end we bypass the const-ness
|
||||
// of this method by grabbing a non-const pointer to "this" and a non-const reference to _doubleHashKey.
|
||||
ShapeInfo* thisPtr = const_cast<ShapeInfo*>(this);
|
||||
DoubleHashKey& key = thisPtr->_doubleHashKey;
|
||||
// The key is not yet cached therefore we must compute it.
|
||||
|
||||
// compute hash1
|
||||
// TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
|
||||
uint32_t primeIndex = 0;
|
||||
key.computeHash((uint32_t)_type, primeIndex++);
|
||||
_doubleHashKey.computeHash((uint32_t)_type, primeIndex++);
|
||||
|
||||
// compute hash1
|
||||
uint32_t hash = key.getHash();
|
||||
uint32_t hash = _doubleHashKey.getHash();
|
||||
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()
|
||||
|
@ -206,10 +203,10 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
|||
primeIndex++);
|
||||
}
|
||||
}
|
||||
key.setHash(hash);
|
||||
_doubleHashKey.setHash(hash);
|
||||
|
||||
// compute hash2
|
||||
hash = key.getHash2();
|
||||
hash = _doubleHashKey.getHash2();
|
||||
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()
|
||||
|
@ -226,7 +223,7 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
|||
hash += ~(floatHash << 10);
|
||||
hash = (hash << 16) | (hash >> 16);
|
||||
}
|
||||
key.setHash2(hash);
|
||||
_doubleHashKey.setHash2(hash);
|
||||
|
||||
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_MESH) {
|
||||
QString url = _url.toString();
|
||||
|
@ -235,8 +232,8 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
|||
QByteArray baUrl = url.toLocal8Bit();
|
||||
const char *cUrl = baUrl.data();
|
||||
uint32_t urlHash = qChecksum(cUrl, baUrl.count());
|
||||
key.setHash(key.getHash() ^ urlHash);
|
||||
key.setHash2(key.getHash2() ^ urlHash);
|
||||
_doubleHashKey.setHash(_doubleHashKey.getHash() ^ urlHash);
|
||||
_doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ urlHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,14 +30,15 @@ enum ShapeType {
|
|||
SHAPE_TYPE_NONE,
|
||||
SHAPE_TYPE_BOX,
|
||||
SHAPE_TYPE_SPHERE,
|
||||
SHAPE_TYPE_PLANE,
|
||||
SHAPE_TYPE_COMPOUND,
|
||||
SHAPE_TYPE_CAPSULE_X,
|
||||
SHAPE_TYPE_CAPSULE_Y,
|
||||
SHAPE_TYPE_CAPSULE_Z,
|
||||
SHAPE_TYPE_CYLINDER_X,
|
||||
SHAPE_TYPE_CYLINDER_Y,
|
||||
SHAPE_TYPE_CYLINDER_Z,
|
||||
SHAPE_TYPE_HULL,
|
||||
SHAPE_TYPE_PLANE,
|
||||
SHAPE_TYPE_COMPOUND,
|
||||
SHAPE_TYPE_MESH
|
||||
};
|
||||
|
||||
|
@ -62,10 +63,13 @@ public:
|
|||
|
||||
const glm::vec3& getHalfExtents() const { return _halfExtents; }
|
||||
const glm::vec3& getOffset() const { return _offset; }
|
||||
uint32_t getNumSubShapes() const;
|
||||
|
||||
PointCollection& getPointCollection() { return _pointCollection; }
|
||||
const PointCollection& getPointCollection() const { return _pointCollection; }
|
||||
uint32_t getNumSubShapes() const;
|
||||
|
||||
TriangleIndices& getTriangleIndices() { return _triangleIndices; }
|
||||
const TriangleIndices& getTriangleIndices() const { return _triangleIndices; }
|
||||
|
||||
int getLargestSubshapePointCount() const;
|
||||
|
||||
|
@ -83,7 +87,7 @@ protected:
|
|||
TriangleIndices _triangleIndices;
|
||||
glm::vec3 _halfExtents = glm::vec3(0.0f);
|
||||
glm::vec3 _offset = glm::vec3(0.0f);
|
||||
DoubleHashKey _doubleHashKey;
|
||||
mutable DoubleHashKey _doubleHashKey;
|
||||
ShapeType _type = SHAPE_TYPE_NONE;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue