Merge pull request #4552 from sethalves/island

Fix various collision-model related bugs
This commit is contained in:
Brad Hefta-Gaub 2015-03-31 15:47:53 -07:00
commit d77333953e
5 changed files with 72 additions and 55 deletions

View file

@ -61,7 +61,7 @@ void RenderableModelEntityItem::remapTextures() {
} }
if (!_model->isLoadedWithTextures()) { if (!_model->isLoadedWithTextures()) {
return; // nothing to do if the model has not yet loaded it's default textures return; // nothing to do if the model has not yet loaded its default textures
} }
if (!_originalTexturesRead && _model->isLoadedWithTextures()) { if (!_originalTexturesRead && _model->isLoadedWithTextures()) {
@ -220,7 +220,7 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
// if we have a URL, then we will want to end up returning a model... // if we have a URL, then we will want to end up returning a model...
if (!getModelURL().isEmpty()) { if (!getModelURL().isEmpty()) {
// if we have a previously allocated model, but it's URL doesn't match // if we have a previously allocated model, but its URL doesn't match
// then we need to let our renderer update our model for us. // then we need to let our renderer update our model for us.
if (_model && QUrl(getModelURL()) != _model->getURL()) { if (_model && QUrl(getModelURL()) != _model->getURL()) {
result = _model = _myRenderer->updateModel(_model, getModelURL(), getCollisionModelURL()); result = _model = _myRenderer->updateModel(_model, getModelURL(), getCollisionModelURL());
@ -293,7 +293,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
} }
if (_model->getCollisionURL().isEmpty()) { if (_model->getCollisionURL().isEmpty()) {
// no model url, so we're ready to compute a shape. // no collision-model url, so we're ready to compute a shape (of type None).
return true; return true;
} }
@ -312,35 +312,30 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
info.setParams(getShapeType(), 0.5f * getDimensions()); info.setParams(getShapeType(), 0.5f * getDimensions());
} else { } else {
const QSharedPointer<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry(); const QSharedPointer<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry();
const FBXGeometry& fbxGeometry = collisionNetworkGeometry->getFBXGeometry(); const FBXGeometry& collisionGeometry = collisionNetworkGeometry->getFBXGeometry();
const QSharedPointer<NetworkGeometry> renderNetworkGeometry = _model->getGeometry();
const FBXGeometry& renderGeometry = renderNetworkGeometry->getFBXGeometry();
AABox aaBox;
_points.clear(); _points.clear();
unsigned int i = 0; unsigned int i = 0;
// the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // 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. // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case.
foreach (const FBXMesh& mesh, fbxGeometry.meshes) { foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
// each meshPart is a convex hull // each meshPart is a convex hull
foreach (const FBXMeshPart &meshPart, mesh.parts) { foreach (const FBXMeshPart &meshPart, mesh.parts) {
QVector<glm::vec3> pointsInPart; QVector<glm::vec3> pointsInPart;
// run through all the triangles and (uniquely) add each point to the hull // run through all the triangles and (uniquely) add each point to the hull
unsigned int triangleCount = meshPart.triangleIndices.size() / 3; unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
assert((unsigned int)meshPart.triangleIndices.size() == triangleCount*3);
for (unsigned int j = 0; j < triangleCount; j++) { for (unsigned int j = 0; j < triangleCount; j++) {
unsigned int p0Index = meshPart.triangleIndices[j*3]; unsigned int p0Index = meshPart.triangleIndices[j*3];
unsigned int p1Index = meshPart.triangleIndices[j*3+1]; unsigned int p1Index = meshPart.triangleIndices[j*3+1];
unsigned int p2Index = meshPart.triangleIndices[j*3+2]; unsigned int p2Index = meshPart.triangleIndices[j*3+2];
assert(p0Index < (unsigned int)mesh.vertices.size());
assert(p1Index < (unsigned int)mesh.vertices.size());
assert(p2Index < (unsigned int)mesh.vertices.size());
glm::vec3 p0 = mesh.vertices[p0Index]; glm::vec3 p0 = mesh.vertices[p0Index];
glm::vec3 p1 = mesh.vertices[p1Index]; glm::vec3 p1 = mesh.vertices[p1Index];
glm::vec3 p2 = mesh.vertices[p2Index]; glm::vec3 p2 = mesh.vertices[p2Index];
aaBox += p0;
aaBox += p1;
aaBox += p2;
if (!pointsInPart.contains(p0)) { if (!pointsInPart.contains(p0)) {
pointsInPart << p0; pointsInPart << p0;
} }
@ -360,18 +355,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
unsigned int p1Index = meshPart.quadIndices[j*4+1]; unsigned int p1Index = meshPart.quadIndices[j*4+1];
unsigned int p2Index = meshPart.quadIndices[j*4+2]; unsigned int p2Index = meshPart.quadIndices[j*4+2];
unsigned int p3Index = meshPart.quadIndices[j*4+3]; unsigned int p3Index = meshPart.quadIndices[j*4+3];
assert(p0Index < (unsigned int)mesh.vertices.size());
assert(p1Index < (unsigned int)mesh.vertices.size());
assert(p2Index < (unsigned int)mesh.vertices.size());
assert(p3Index < (unsigned int)mesh.vertices.size());
glm::vec3 p0 = mesh.vertices[p0Index]; glm::vec3 p0 = mesh.vertices[p0Index];
glm::vec3 p1 = mesh.vertices[p1Index]; glm::vec3 p1 = mesh.vertices[p1Index];
glm::vec3 p2 = mesh.vertices[p2Index]; glm::vec3 p2 = mesh.vertices[p2Index];
glm::vec3 p3 = mesh.vertices[p3Index]; glm::vec3 p3 = mesh.vertices[p3Index];
aaBox += p0;
aaBox += p1;
aaBox += p2;
aaBox += p3;
if (!pointsInPart.contains(p0)) { if (!pointsInPart.contains(p0)) {
pointsInPart << p0; pointsInPart << p0;
} }
@ -386,6 +373,11 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
} }
} }
if (pointsInPart.size() == 0) {
qDebug() << "Warning -- meshPart has no faces";
continue;
}
// add next convex hull // add next convex hull
QVector<glm::vec3> newMeshPoints; QVector<glm::vec3> newMeshPoints;
_points << newMeshPoints; _points << newMeshPoints;
@ -394,11 +386,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
} }
} }
// make sure we aren't about to divide by zero // We expect that the collision model will have the same units and will be displaced
glm::vec3 aaBoxDim = aaBox.getDimensions(); // from its origin in the same way the visual model is. The visual model has
aaBoxDim = glm::clamp(aaBoxDim, glm::vec3(FLT_EPSILON), aaBoxDim); // been centered and probably scaled. We take the scaling and offset which were applied
// to the visual model and apply them to the collision model (without regard for the
// collision model's extents).
glm::vec3 scale = _dimensions / renderGeometry.getUnscaledMeshExtents().size();
glm::vec3 scale = _dimensions / aaBoxDim;
// multiply each point by scale before handing the point-set off to the physics engine // multiply each point by scale before handing the point-set off to the physics engine
for (int i = 0; i < _points.size(); i++) { for (int i = 0; i < _points.size(); i++) {
for (int j = 0; j < _points[i].size(); j++) { for (int j = 0; j < _points[i].size(); j++) {

View file

@ -27,7 +27,8 @@ public:
enum SpecialToken { enum SpecialToken {
NO_TOKEN = -1, NO_TOKEN = -1,
NO_PUSHBACKED_TOKEN = -1, NO_PUSHBACKED_TOKEN = -1,
DATUM_TOKEN = 0x100 DATUM_TOKEN = 0x100,
COMMENT_TOKEN = 0x101
}; };
int nextToken(); int nextToken();
const QByteArray& getDatum() const { return _datum; } const QByteArray& getDatum() const { return _datum; }
@ -35,11 +36,13 @@ public:
void skipLine() { _device->readLine(); } void skipLine() { _device->readLine(); }
void pushBackToken(int token) { _pushedBackToken = token; } void pushBackToken(int token) { _pushedBackToken = token; }
void ungetChar(char ch) { _device->ungetChar(ch); } void ungetChar(char ch) { _device->ungetChar(ch); }
const QString getComment() const { return _comment; }
private: private:
QIODevice* _device; QIODevice* _device;
QByteArray _datum; QByteArray _datum;
int _pushedBackToken; int _pushedBackToken;
QString _comment;
}; };
@ -56,9 +59,11 @@ int OBJTokenizer::nextToken() {
continue; // skip whitespace continue; // skip whitespace
} }
switch (ch) { switch (ch) {
case '#': case '#': {
_device->readLine(); // skip the comment _comment = _device->readLine(); // skip the comment
break; qDebug() << "COMMENT:" << _comment;
return COMMENT_TOKEN;
}
case '\"': case '\"':
_datum = ""; _datum = "";
@ -104,7 +109,8 @@ bool OBJTokenizer::isNextTokenFloat() {
} }
bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
FBXGeometry &geometry, QVector<glm::vec3>& faceNormals, QVector<int>& faceNormalIndexes) { FBXGeometry &geometry, QVector<glm::vec3>& faceNormals, QVector<int>& faceNormalIndexes,
float& scaleGuess) {
FBXMesh &mesh = geometry.meshes[0]; FBXMesh &mesh = geometry.meshes[0];
mesh.parts.append(FBXMeshPart()); mesh.parts.append(FBXMeshPart());
FBXMeshPart &meshPart = mesh.parts.last(); FBXMeshPart &meshPart = mesh.parts.last();
@ -128,7 +134,17 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0)); meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0));
while (true) { while (true) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { int tokenType = tokenizer.nextToken();
if (tokenType == OBJTokenizer::COMMENT_TOKEN) {
if (tokenizer.getComment().contains("This file uses centimeters as units")) {
scaleGuess = 1.0f / 100.0f;
}
if (tokenizer.getComment().contains("This file uses millimeters as units")) {
scaleGuess = 1.0f / 1000.0f;
}
continue;
}
if (tokenType != OBJTokenizer::DATUM_TOKEN) {
result = false; result = false;
break; break;
} }
@ -192,6 +208,7 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
while (true) { while (true) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
if (indices.count() == 0) { if (indices.count() == 0) {
// nonsense, bail out.
goto done; goto done;
} }
break; break;
@ -266,22 +283,22 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
} }
} else { } else {
// something we don't (yet) care about // something we don't (yet) care about
qDebug() << "OBJ parser is skipping a line with" << token; // qDebug() << "OBJ parser is skipping a line with" << token;
tokenizer.skipLine(); tokenizer.skipLine();
} }
} }
done: done:
if (meshPart.triangleIndices.size() == 0 && meshPart.quadIndices.size() == 0) {
// empty mesh?
mesh.parts.pop_back();
}
return result; return result;
} }
FBXGeometry extractOBJGeometry(const FBXNode& node, const QVariantHash& mapping) {
FBXGeometry geometry;
return geometry;
}
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) { FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) {
QBuffer buffer(const_cast<QByteArray*>(&model)); QBuffer buffer(const_cast<QByteArray*>(&model));
buffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);
@ -294,24 +311,30 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
OBJTokenizer tokenizer(device); OBJTokenizer tokenizer(device);
QVector<int> faceNormalIndexes; QVector<int> faceNormalIndexes;
QVector<glm::vec3> faceNormals; QVector<glm::vec3> faceNormals;
float scaleGuess = 1.0f;
faceNormalIndexes.clear(); faceNormalIndexes.clear();
geometry.meshExtents.reset(); geometry.meshExtents.reset();
geometry.meshes.append(FBXMesh()); geometry.meshes.append(FBXMesh());
try { try {
// call parseOBJGroup as long as it's returning true. Each successful call will // call parseOBJGroup as long as it's returning true. Each successful call will
// add a new meshPart to the geometry's single mesh. // add a new meshPart to the geometry's single mesh.
bool success = true; bool success = true;
while (success) { while (success) {
success = parseOBJGroup(tokenizer, mapping, geometry, faceNormals, faceNormalIndexes); success = parseOBJGroup(tokenizer, mapping, geometry, faceNormals, faceNormalIndexes, scaleGuess);
} }
FBXMesh &mesh = geometry.meshes[0]; FBXMesh &mesh = geometry.meshes[0];
// if we got a hint about units, scale all the points
if (scaleGuess != 1.0f) {
for (int i = 0; i < mesh.vertices.size(); i++) {
mesh.vertices[i] *= scaleGuess;
}
}
mesh.meshExtents.reset(); mesh.meshExtents.reset();
foreach (const glm::vec3& vertex, mesh.vertices) { foreach (const glm::vec3& vertex, mesh.vertices) {
mesh.meshExtents.addPoint(vertex); mesh.meshExtents.addPoint(vertex);

View file

@ -70,6 +70,7 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
bool delayLoad, void* extra) { bool delayLoad, void* extra) {
QSharedPointer<Resource> resource = _resources.value(url); QSharedPointer<Resource> resource = _resources.value(url);
if (!resource.isNull()) { if (!resource.isNull()) {
removeUnusedResource(resource);
return resource; return resource;
} }
@ -83,16 +84,14 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
return getResource(fallback, QUrl(), delayLoad); return getResource(fallback, QUrl(), delayLoad);
} }
if (resource.isNull()) { resource = createResource(url, fallback.isValid() ?
resource = createResource(url, fallback.isValid() ? getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra); resource->setSelf(resource);
resource->setSelf(resource); resource->setCache(this);
resource->setCache(this); _resources.insert(url, resource);
_resources.insert(url, resource); removeUnusedResource(resource);
resource->ensureLoading();
} else {
removeUnusedResource(resource);
}
return resource; return resource;
} }

View file

@ -1775,10 +1775,10 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
return getResource(url, fallback, delayLoad, NULL).staticCast<NetworkGeometry>(); return getResource(url, fallback, delayLoad, NULL).staticCast<NetworkGeometry>();
} }
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { bool delayLoad, const void* extra) {
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad), QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad),
&Resource::allReferencesCleared); &Resource::allReferencesCleared);
geometry->setLODParent(geometry); geometry->setLODParent(geometry);
return geometry.staticCast<Resource>(); return geometry.staticCast<Resource>();
} }

View file

@ -48,7 +48,7 @@ public:
void rotate(const glm::quat& rotation); void rotate(const glm::quat& rotation);
glm::vec3 size() const { return maximum - minimum; } glm::vec3 size() const { return maximum - minimum; }
float largestDimension () const {glm::vec3 s = size(); return glm::max(s[0], s[1], s[2]); } float largestDimension() const {glm::vec3 s = size(); return glm::max(s[0], s[1], s[2]); }
/// \return new Extents which is original rotated around orign by rotation /// \return new Extents which is original rotated around orign by rotation
Extents getRotated(const glm::quat& rotation) const { Extents getRotated(const glm::quat& rotation) const {