Merge pull request #8192 from AndrewMeadows/simple-hull-shapes

experimental ModelEntityItem collision shape options (no visual debugging)
This commit is contained in:
Brad Hefta-Gaub 2016-07-08 08:55:11 -07:00 committed by GitHub
commit 5f71f77445
8 changed files with 113 additions and 74 deletions

View file

@ -430,7 +430,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
// check to see if when we added our models to the scene they were ready, if they were not ready, then // check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene // fix them up in the scene
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0
&& getShapeType() == SHAPE_TYPE_COMPOUND;
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) { if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
_showCollisionHull = shouldShowCollisionHull; _showCollisionHull = shouldShowCollisionHull;
render::PendingChanges pendingChanges; render::PendingChanges pendingChanges;
@ -600,7 +601,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
// the model is still being downloaded. // the model is still being downloaded.
return false; return false;
} else if (type == SHAPE_TYPE_STATIC_MESH) { } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
return (_model && _model->isLoaded()); return (_model && _model->isLoaded());
} }
return true; return true;
@ -614,7 +615,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
// should never fall in here when collision model not fully loaded // should never fall in here when collision model not fully loaded
// hence we assert that all geometries exist and are loaded // hence we assert that all geometries exist and are loaded
assert(_model->isLoaded() && _model->isCollisionLoaded()); assert(_model && _model->isLoaded() && _model->isCollisionLoaded());
const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry(); const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry();
ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
@ -698,14 +699,19 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
} }
} }
info.setParams(type, dimensions, _compoundShapeURL); info.setParams(type, dimensions, _compoundShapeURL);
} else if (type == SHAPE_TYPE_STATIC_MESH) { } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
updateModelBounds();
// should never fall in here when model not fully loaded
assert(_model && _model->isLoaded());
// compute meshPart local transforms // compute meshPart local transforms
QVector<glm::mat4> localTransforms; QVector<glm::mat4> localTransforms;
const FBXGeometry& geometry = _model->getFBXGeometry(); const FBXGeometry& fbxGeometry = _model->getFBXGeometry();
int numberOfMeshes = geometry.meshes.size(); int numberOfMeshes = fbxGeometry.meshes.size();
int totalNumVertices = 0; int totalNumVertices = 0;
for (int i = 0; i < numberOfMeshes; i++) { for (int i = 0; i < numberOfMeshes; i++) {
const FBXMesh& mesh = geometry.meshes.at(i); const FBXMesh& mesh = fbxGeometry.meshes.at(i);
if (mesh.clusters.size() > 0) { if (mesh.clusters.size() > 0) {
const FBXCluster& cluster = mesh.clusters.at(0); const FBXCluster& cluster = mesh.clusters.at(0);
auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex); auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex);
@ -723,30 +729,42 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
return; return;
} }
updateModelBounds(); auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
int32_t numMeshes = (int32_t)(meshes.size());
// should never fall in here when collision model not fully loaded
assert(_model->isLoaded());
ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
pointCollection.clear(); pointCollection.clear();
if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
ShapeInfo::PointList points; pointCollection.resize(numMeshes);
ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); } else {
auto& meshes = _model->getGeometry()->getGeometry()->getMeshes(); pointCollection.resize(1);
}
Extents extents; Extents extents;
int meshCount = 0; int meshCount = 0;
int pointListIndex = 0;
for (auto& mesh : meshes) { for (auto& mesh : meshes) {
const gpu::BufferView& vertices = mesh->getVertexBuffer(); const gpu::BufferView& vertices = mesh->getVertexBuffer();
const gpu::BufferView& indices = mesh->getIndexBuffer(); const gpu::BufferView& indices = mesh->getIndexBuffer();
const gpu::BufferView& parts = mesh->getPartBuffer(); const gpu::BufferView& parts = mesh->getPartBuffer();
ShapeInfo::PointList& points = pointCollection[pointListIndex];
// reserve room
int32_t sizeToReserve = (int32_t)(vertices.getNumElements());
if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
// a list of points for each mesh
pointListIndex++;
} else {
// only one list of points
sizeToReserve += (int32_t)((gpu::Size)points.size());
}
points.reserve(sizeToReserve);
// copy points // copy points
const glm::mat4& localTransform = localTransforms[meshCount];
uint32_t meshIndexOffset = (uint32_t)points.size(); uint32_t meshIndexOffset = (uint32_t)points.size();
const glm::mat4& localTransform = localTransforms[meshCount];
gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertices.cbegin<const glm::vec3>(); gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertices.cbegin<const glm::vec3>();
points.reserve((int32_t)((gpu::Size)points.size() + vertices.getNumElements()));
while (vertexItr != vertices.cend<const glm::vec3>()) { while (vertexItr != vertices.cend<const glm::vec3>()) {
glm::vec3 point = extractTranslation(localTransform * glm::translate(*vertexItr)); glm::vec3 point = extractTranslation(localTransform * glm::translate(*vertexItr));
points.push_back(point); points.push_back(point);
@ -754,55 +772,57 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
++vertexItr; ++vertexItr;
} }
// copy triangleIndices if (type == SHAPE_TYPE_STATIC_MESH) {
triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); // copy into triangleIndices
gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>(); ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
while (partItr != parts.cend<const model::Mesh::Part>()) { triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements()));
gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
if (partItr->_topology == model::Mesh::TRIANGLES) { while (partItr != parts.cend<const model::Mesh::Part>()) {
assert(partItr->_numIndices % 3 == 0); if (partItr->_topology == model::Mesh::TRIANGLES) {
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex; assert(partItr->_numIndices % 3 == 0);
auto indexEnd = indexItr + partItr->_numIndices; auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
while (indexItr != indexEnd) { auto indexEnd = indexItr + partItr->_numIndices;
triangleIndices.push_back(*indexItr + meshIndexOffset); while (indexItr != indexEnd) {
++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++) + meshIndexOffset);
// the rest use previous and next index
uint32_t triangleCount = 1;
while (indexItr != indexEnd) {
if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) {
if (triangleCount % 2 == 0) {
// even triangles use first two indices in order
triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset);
triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset);
} else {
// odd triangles swap order of first two indices
triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset);
triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset);
}
triangleIndices.push_back(*indexItr + meshIndexOffset); triangleIndices.push_back(*indexItr + meshIndexOffset);
++triangleCount; ++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++) + meshIndexOffset);
// the rest use previous and next index
uint32_t triangleCount = 1;
while (indexItr != indexEnd) {
if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) {
if (triangleCount % 2 == 0) {
// even triangles use first two indices in order
triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset);
triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset);
} else {
// odd triangles swap order of first two indices
triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset);
triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset);
}
triangleIndices.push_back(*indexItr + meshIndexOffset);
++triangleCount;
}
++indexItr;
} }
++indexItr;
} }
++partItr;
} }
++partItr;
} }
++meshCount; ++meshCount;
} }
@ -815,12 +835,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
scaleToFit[i] = 1.0f; scaleToFit[i] = 1.0f;
} }
} }
for (int i = 0; i < points.size(); ++i) { for (auto points : pointCollection) {
points[i] = (points[i] * scaleToFit); for (int i = 0; i < points.size(); ++i) {
points[i] = (points[i] * scaleToFit);
}
} }
pointCollection.push_back(points); info.setParams(type, 0.5f * dimensions, _modelURL);
info.setParams(SHAPE_TYPE_STATIC_MESH, 0.5f * dimensions, _modelURL);
} else { } else {
ModelEntityItem::computeShapeInfo(info); ModelEntityItem::computeShapeInfo(info);
info.setParams(type, 0.5f * dimensions); info.setParams(type, 0.5f * dimensions);

View file

@ -101,6 +101,8 @@ const char* shapeTypeNames[] = {
"hull", "hull",
"plane", "plane",
"compound", "compound",
"simple-hull",
"simple-compound",
"static-mesh" "static-mesh"
}; };
@ -123,6 +125,8 @@ void buildStringToShapeTypeLookup() {
addShapeType(SHAPE_TYPE_HULL); addShapeType(SHAPE_TYPE_HULL);
addShapeType(SHAPE_TYPE_PLANE); addShapeType(SHAPE_TYPE_PLANE);
addShapeType(SHAPE_TYPE_COMPOUND); addShapeType(SHAPE_TYPE_COMPOUND);
addShapeType(SHAPE_TYPE_SIMPLE_HULL);
addShapeType(SHAPE_TYPE_SIMPLE_COMPOUND);
addShapeType(SHAPE_TYPE_STATIC_MESH); addShapeType(SHAPE_TYPE_STATIC_MESH);
} }

View file

@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityAdd: case PacketType::EntityAdd:
case PacketType::EntityEdit: case PacketType::EntityEdit:
case PacketType::EntityData: case PacketType::EntityData:
return VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH; return VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS;
case PacketType::AvatarIdentity: case PacketType::AvatarIdentity:
case PacketType::AvatarData: case PacketType::AvatarData:
case PacketType::BulkAvatarData: case PacketType::BulkAvatarData:

View file

@ -181,6 +181,7 @@ const PacketVersion VERSION_ENTITIES_NO_FLY_ZONES = 58;
const PacketVersion VERSION_ENTITIES_MORE_SHAPES = 59; const PacketVersion VERSION_ENTITIES_MORE_SHAPES = 59;
const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60; const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60;
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61; const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61;
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62;
enum class AvatarMixerPacketVersion : PacketVersion { enum class AvatarMixerPacketVersion : PacketVersion {
TranslationSupport = 17, TranslationSupport = 17,

View file

@ -204,7 +204,7 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) {
if (numIndices < INT16_MAX) { if (numIndices < INT16_MAX) {
int16_t* indices = static_cast<int16_t*>((void*)(mesh.m_triangleIndexBase)); int16_t* indices = static_cast<int16_t*>((void*)(mesh.m_triangleIndexBase));
for (int32_t i = 0; i < numIndices; ++i) { for (int32_t i = 0; i < numIndices; ++i) {
indices[i] = triangleIndices[i]; indices[i] = (int16_t)triangleIndices[i];
} }
} else { } else {
int32_t* indices = static_cast<int32_t*>((void*)(mesh.m_triangleIndexBase)); int32_t* indices = static_cast<int32_t*>((void*)(mesh.m_triangleIndexBase));
@ -257,7 +257,9 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
shape = new btCapsuleShape(radius, height); shape = new btCapsuleShape(radius, height);
} }
break; break;
case SHAPE_TYPE_COMPOUND: { case SHAPE_TYPE_COMPOUND:
case SHAPE_TYPE_SIMPLE_HULL:
case SHAPE_TYPE_SIMPLE_COMPOUND: {
const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
uint32_t numSubShapes = info.getNumSubShapes(); uint32_t numSubShapes = info.getNumSubShapes();
if (numSubShapes == 1) { if (numSubShapes == 1) {

View file

@ -83,12 +83,19 @@ void ShapeInfo::setOffset(const glm::vec3& offset) {
} }
uint32_t ShapeInfo::getNumSubShapes() const { uint32_t ShapeInfo::getNumSubShapes() const {
if (_type == SHAPE_TYPE_NONE) { switch (_type) {
return 0; case SHAPE_TYPE_NONE:
} else if (_type == SHAPE_TYPE_COMPOUND) { return 0;
return _pointCollection.size(); case SHAPE_TYPE_COMPOUND:
case SHAPE_TYPE_SIMPLE_COMPOUND:
return _pointCollection.size();
case SHAPE_TYPE_SIMPLE_HULL:
case SHAPE_TYPE_STATIC_MESH:
assert(_pointCollection.size() == 1);
// yes fall through to default
default:
return 1;
} }
return 1;
} }
int ShapeInfo::getLargestSubshapePointCount() const { int ShapeInfo::getLargestSubshapePointCount() const {

View file

@ -39,6 +39,8 @@ enum ShapeType {
SHAPE_TYPE_HULL, SHAPE_TYPE_HULL,
SHAPE_TYPE_PLANE, SHAPE_TYPE_PLANE,
SHAPE_TYPE_COMPOUND, SHAPE_TYPE_COMPOUND,
SHAPE_TYPE_SIMPLE_HULL,
SHAPE_TYPE_SIMPLE_COMPOUND,
SHAPE_TYPE_STATIC_MESH SHAPE_TYPE_STATIC_MESH
}; };

View file

@ -1646,6 +1646,8 @@
<option value="box">Box</option> <option value="box">Box</option>
<option value="sphere">Sphere</option> <option value="sphere">Sphere</option>
<option value="compound">Compound</option> <option value="compound">Compound</option>
<option value="simple-hull">One Hull</option>
<option value="simple-compound">Hull Per Submesh</option>
<option value="static-mesh">Static Mesh</option> <option value="static-mesh">Static Mesh</option>
</select> </select>
</div> </div>