diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 7b51283bac..7b3b3c3efe 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -599,11 +599,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { ShapeType type = getShapeType(); - if (type != SHAPE_TYPE_COMPOUND) { - ModelEntityItem::computeShapeInfo(info); - info.setParams(type, 0.5f * getDimensions()); - adjustShapeInfoByRegistration(info); - } else { + if (type == SHAPE_TYPE_COMPOUND) { updateModelBounds(); // should never fall in here when collision model not fully loaded @@ -612,25 +608,27 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { const FBXGeometry& renderGeometry = _model->getFBXGeometry(); const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry(); - _points.clear(); - unsigned int i = 0; + QVector>& points = info.getPoints(); + points.clear(); + uint32_t i = 0; // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. + const uint32_t TRIANGLE_STRIDE = 3; + const uint32_t QUAD_STRIDE = 4; foreach (const FBXMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull foreach (const FBXMeshPart &meshPart, mesh.parts) { - QVector pointsInPart; + points.push_back(QVector()); + QVector& pointsInPart = points[i]; // run through all the triangles and (uniquely) add each point to the hull - unsigned int triangleCount = meshPart.triangleIndices.size() / 3; - for (unsigned int j = 0; j < triangleCount; j++) { - unsigned int p0Index = meshPart.triangleIndices[j*3]; - unsigned int p1Index = meshPart.triangleIndices[j*3+1]; - unsigned int p2Index = meshPart.triangleIndices[j*3+2]; - glm::vec3 p0 = mesh.vertices[p0Index]; - glm::vec3 p1 = mesh.vertices[p1Index]; - glm::vec3 p2 = mesh.vertices[p2Index]; + uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); + assert(numIndices % TRIANGLE_STRIDE == 0); + for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) { + glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]]; + glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]]; + glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[j + 2]]; if (!pointsInPart.contains(p0)) { pointsInPart << p0; } @@ -643,17 +641,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } // run through all the quads and (uniquely) add each point to the hull - unsigned int quadCount = meshPart.quadIndices.size() / 4; - assert((unsigned int)meshPart.quadIndices.size() == quadCount*4); - for (unsigned int j = 0; j < quadCount; j++) { - unsigned int p0Index = meshPart.quadIndices[j*4]; - unsigned int p1Index = meshPart.quadIndices[j*4+1]; - unsigned int p2Index = meshPart.quadIndices[j*4+2]; - unsigned int p3Index = meshPart.quadIndices[j*4+3]; - glm::vec3 p0 = mesh.vertices[p0Index]; - glm::vec3 p1 = mesh.vertices[p1Index]; - glm::vec3 p2 = mesh.vertices[p2Index]; - glm::vec3 p3 = mesh.vertices[p3Index]; + numIndices = (uint32_t)meshPart.quadIndices.size(); + assert(numIndices % QUAD_STRIDE == 0); + for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) { + glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]]; + glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]]; + glm::vec3 p2 = mesh.vertices[meshPart.quadIndices[j + 2]]; + glm::vec3 p3 = mesh.vertices[meshPart.quadIndices[j + 3]]; if (!pointsInPart.contains(p0)) { pointsInPart << p0; } @@ -670,14 +664,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { if (pointsInPart.size() == 0) { qCDebug(entitiesrenderer) << "Warning -- meshPart has no faces"; + points.pop_back(); continue; } - - // add next convex hull - QVector newMeshPoints; - _points << newMeshPoints; - // add points to the new convex hull - _points[i++] << pointsInPart; + ++i; } } @@ -691,23 +681,26 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // 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; - for (int i = 0; i < _points.size(); i++) { - for (int j = 0; j < _points[i].size(); j++) { + for (int i = 0; i < points.size(); i++) { + for (int j = 0; j < points[i].size(); j++) { // compensate for registration - _points[i][j] += _model->getOffset(); + points[i][j] += _model->getOffset(); // scale so the collision points match the model points - _points[i][j] *= scale; + points[i][j] *= scale; // this next subtraction is done so we can give info the offset, which will cause // the shape-key to change. - _points[i][j] -= _model->getOffset(); - box += _points[i][j]; + points[i][j] -= _model->getOffset(); + box += points[i][j]; } } glm::vec3 collisionModelDimensions = box.getDimensions(); info.setParams(type, collisionModelDimensions, _compoundShapeURL); - info.setConvexHulls(_points); info.setOffset(_model->getOffset()); + } else { + ModelEntityItem::computeShapeInfo(info); + info.setParams(type, 0.5f * getDimensions()); + adjustShapeInfoByRegistration(info); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index d2de45f538..339c907532 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -103,7 +103,6 @@ private: QVariantMap _currentTextures; QVariantMap _originalTextures; bool _originalTexturesRead = false; - QVector> _points; bool _dimensionsInitialized = true; AnimationPropertyGroup _renderAnimationProperties; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index ba39727ff9..89bf9f1a21 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -88,7 +88,7 @@ void EntityItemProperties::setLastEdited(quint64 usecTime) { _lastEdited = usecTime > _created ? usecTime : _created; } -const char* shapeTypeNames[] = {"none", "box", "sphere", "ellipsoid", "plane", "compound", "capsule-x", +const char* shapeTypeNames[] = {"none", "box", "sphere", "plane", "compound", "capsule-x", "capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"}; QHash stringToShapeTypeLookup; @@ -101,7 +101,6 @@ void buildStringToShapeTypeLookup() { addShapeType(SHAPE_TYPE_NONE); addShapeType(SHAPE_TYPE_BOX); addShapeType(SHAPE_TYPE_SPHERE); - addShapeType(SHAPE_TYPE_ELLIPSOID); addShapeType(SHAPE_TYPE_PLANE); addShapeType(SHAPE_TYPE_COMPOUND); addShapeType(SHAPE_TYPE_CAPSULE_X); diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 4d63562cf7..6a5ef20bac 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -60,7 +60,7 @@ class LineEntityItem : public EntityItem { const QVector& getLinePoints() const{ return _points; } - virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } + virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } // never have a ray intersection pick a LineEntityItem. virtual bool supportsDetailedRayIntersection() const { return true; } diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index cf7b14dbf1..3231e7c5e1 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -78,7 +78,7 @@ class PolyLineEntityItem : public EntityItem { virtual bool needsToCallUpdate() const { return true; } - virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } + virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } // never have a ray intersection pick a PolyLineEntityItem. virtual bool supportsDetailedRayIntersection() const { return true; } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 71b919b7ee..d667d1075d 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -17,6 +17,54 @@ #include "ShapeFactory.h" #include "BulletUtil.h" +// These are the same normalized directions used by the btShapeHull class. +// 12 points for the face centers of a duodecohedron plus another 30 points +// for the midpoints the edges, for a total of 42. +const uint32_t NUM_UNIT_SPHERE_DIRECTIONS = 42; +static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = { + btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)), + btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)), + btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)), + btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)), + btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)), + btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)), + btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)), + btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)), + btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)), + btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)), + btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)), + btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)), + btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)), + btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)), + btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)), + btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)), + btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)), + btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)), + btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)), + btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)), + btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)), + btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)), + btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)), + btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)), + btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)), + btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)), + btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)), + btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)), + btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)), + btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)), + btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)), + btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)), + btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)), + btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)), + btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)), + btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)), + btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)), + btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)), + btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)), + btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)), + btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)), + btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654)) +}; btConvexHullShape* ShapeFactory::createConvexHull(const QVector& points) { @@ -66,15 +114,40 @@ btConvexHullShape* ShapeFactory::createConvexHull(const QVector& poin hull->addPoint(btVector3(correctedPoint[0], correctedPoint[1], correctedPoint[2]), false); } - if (points.size() > MAX_HULL_POINTS) { - // create hull approximation - btShapeHull shapeHull(hull); - shapeHull.buildHull(margin); + uint32_t numPoints = (uint32_t)hull->getNumPoints(); + if (numPoints > MAX_HULL_POINTS) { + // we have too many points, so we compute point projections along canonical unit vectors + // and keep the those that project the farthest + btVector3 btCenter = glmToBullet(center); + btVector3* shapePoints = hull->getUnscaledPoints(); + std::vector finalIndices; + finalIndices.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + uint32_t bestIndex = 0; + btScalar maxDistance = _unitSphereDirections[i].dot(shapePoints[0] - btCenter); + for (uint32_t j = 1; j < numPoints; ++j) { + btScalar distance = _unitSphereDirections[i].dot(shapePoints[j] - btCenter); + if (distance > maxDistance) { + maxDistance = distance; + bestIndex = j; + } + } + bool keep = true; + for (uint32_t j = 0; j < finalIndices.size(); ++j) { + if (finalIndices[j] == bestIndex) { + keep = false; + break; + } + } + if (keep) { + finalIndices.push_back(bestIndex); + } + } + // we cannot copy Bullet shapes so we must create a new one... btConvexHullShape* newHull = new btConvexHullShape(); - const btVector3* newPoints = shapeHull.getVertexPointer(); - for (int i = 0; i < shapeHull.numVertices(); ++i) { - newHull->addPoint(newPoints[i], false); + for (uint32_t i = 0; i < finalIndices.size(); ++i) { + newHull->addPoint(shapePoints[finalIndices[i]], false); } // ...and delete the old one delete hull; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8011b0c4c8..ded1184c24 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -35,7 +35,46 @@ int vec3VectorTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" -model::MaterialPointer Model::_collisionHullMaterial; +const int NUM_COLLISION_HULL_COLORS = 24; +std::vector _collisionHullMaterials; + +void initCollisionHullMaterials() { + // generates bright colors in red, green, blue, yellow, magenta, and cyan spectrums + // (no browns, greys, or dark shades) + float component[NUM_COLLISION_HULL_COLORS] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.2f, 0.4f, 0.6f, 0.8f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 0.8f, 0.6f, 0.4f, 0.2f + }; + _collisionHullMaterials.reserve(NUM_COLLISION_HULL_COLORS); + + // each component gets the same cuve + // but offset by a multiple of one third the full width + int numComponents = 3; + int sectionWidth = NUM_COLLISION_HULL_COLORS / numComponents; + int greenPhase = sectionWidth; + int bluePhase = 2 * sectionWidth; + + // we stride through the colors to scatter adjacent shades + // so they don't tend to group together for large models + for (int i = 0; i < sectionWidth; ++i) { + for (int j = 0; j < numComponents; ++j) { + model::MaterialPointer material; + material = std::make_shared(); + int index = j * sectionWidth + i; + float red = component[index]; + float green = component[(index + greenPhase) % NUM_COLLISION_HULL_COLORS]; + float blue = component[(index + bluePhase) % NUM_COLLISION_HULL_COLORS]; + material->setAlbedo(glm::vec3(red, green, blue)); + material->setMetallic(0.02f); + material->setRoughness(0.5f); + _collisionHullMaterials.push_back(material); + } + } +} Model::Model(RigPointer rig, QObject* parent) : QObject(parent), @@ -1217,13 +1256,10 @@ void Model::segregateMeshGroups() { int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { if (showingCollisionHull) { - if (!_collisionHullMaterial) { - _collisionHullMaterial = std::make_shared(); - _collisionHullMaterial->setAlbedo(glm::vec3(1.0f, 0.5f, 0.0f)); - _collisionHullMaterial->setMetallic(0.02f); - _collisionHullMaterial->setRoughness(0.5f); + if (_collisionHullMaterials.empty()) { + initCollisionHullMaterials(); } - _collisionRenderItemsSet << std::make_shared(networkMesh, partIndex, _collisionHullMaterial, transform, offset); + _collisionRenderItemsSet << std::make_shared(networkMesh, partIndex, _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS], transform, offset); } else { _modelMeshRenderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); } diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 0974c88e73..9c1e5c3816 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -23,7 +23,6 @@ void ShapeInfo::clear() { void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { _type = type; - _points.clear(); switch(type) { case SHAPE_TYPE_NONE: _halfExtents = glm::vec3(0.0f); @@ -52,7 +51,6 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { _url = ""; _type = SHAPE_TYPE_BOX; _halfExtents = halfExtents; - _points.clear(); _doubleHashKey.clear(); } @@ -60,15 +58,6 @@ void ShapeInfo::setSphere(float radius) { _url = ""; _type = SHAPE_TYPE_SPHERE; _halfExtents = glm::vec3(radius, radius, radius); - _points.clear(); - _doubleHashKey.clear(); -} - -void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) { - _url = ""; - _type = SHAPE_TYPE_ELLIPSOID; - _halfExtents = halfExtents; - _points.clear(); _doubleHashKey.clear(); } @@ -82,7 +71,6 @@ void ShapeInfo::setCapsuleY(float radius, float halfHeight) { _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; _halfExtents = glm::vec3(radius, halfHeight, radius); - _points.clear(); _doubleHashKey.clear(); } @@ -146,10 +134,6 @@ bool ShapeInfo::contains(const glm::vec3& point) const { switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; - case SHAPE_TYPE_ELLIPSOID: { - glm::vec3 scaledPoint = glm::abs(point) / _halfExtents; - return glm::length(scaledPoint) <= 1.0f; - } case SHAPE_TYPE_CYLINDER_X: return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z; case SHAPE_TYPE_CYLINDER_Y: diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 1632d22450..c853666d90 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -30,7 +30,6 @@ enum ShapeType { SHAPE_TYPE_NONE, SHAPE_TYPE_BOX, SHAPE_TYPE_SPHERE, - SHAPE_TYPE_ELLIPSOID, SHAPE_TYPE_PLANE, SHAPE_TYPE_COMPOUND, SHAPE_TYPE_CAPSULE_X, @@ -39,7 +38,7 @@ enum ShapeType { SHAPE_TYPE_CYLINDER_X, SHAPE_TYPE_CYLINDER_Y, SHAPE_TYPE_CYLINDER_Z, - SHAPE_TYPE_LINE + SHAPE_TYPE_STATIC_MESH }; class ShapeInfo { @@ -50,7 +49,6 @@ public: void setParams(ShapeType type, const glm::vec3& halfExtents, QString url=""); void setBox(const glm::vec3& halfExtents); void setSphere(float radius); - void setEllipsoid(const glm::vec3& halfExtents); void setConvexHulls(const QVector>& points); void setCapsuleY(float radius, float halfHeight); void setOffset(const glm::vec3& offset); @@ -60,10 +58,10 @@ public: const glm::vec3& getHalfExtents() const { return _halfExtents; } const glm::vec3& getOffset() const { return _offset; } + QVector>& getPoints() { return _points; } const QVector>& getPoints() const { return _points; } uint32_t getNumSubShapes() const; - void clearPoints () { _points.clear(); } void appendToPoints (const QVector& newPoints) { _points << newPoints; } int getMaxNumPoints() const;