Merge pull request #10178 from sethalves/fix-compound-collision-hulls

Fix compound collision hulls
This commit is contained in:
Seth Alves 2017-04-10 15:28:49 -07:00 committed by GitHub
commit b9afcd3f0a
8 changed files with 75 additions and 28 deletions

View file

@ -14,6 +14,7 @@
#include <QJsonDocument>
#include <QtCore/QThread>
#include <QUrlQuery>
#include <glm/gtx/transform.hpp>
#include <AbstractViewStateInterface.h>
@ -611,7 +612,7 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) {
ModelEntityItem::setShapeType(type);
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
if (!_compoundShapeResource && !getCompoundShapeURL().isEmpty()) {
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(getCompoundShapeURL());
_compoundShapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(getCompoundShapeURL());
}
} else if (_compoundShapeResource && !getCompoundShapeURL().isEmpty()) {
// the compoundURL has been set but the shapeType does not agree
@ -620,8 +621,16 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) {
}
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
// because the caching system only allows one Geometry per url, and because this url might also be used
// as a visual model, we need to change this url in some way. We add a "collision-hull" query-arg so it
// will end up in a different hash-key in ResourceCache. TODO: It would be better to use the same URL and
// parse it twice.
auto currentCompoundShapeURL = getCompoundShapeURL();
ModelEntityItem::setCompoundShapeURL(url);
QUrl hullURL(url);
QUrlQuery queryArgs(hullURL);
queryArgs.addQueryItem("collision-hull", "");
hullURL.setQuery(queryArgs);
ModelEntityItem::setCompoundShapeURL(hullURL.toString());
if (getCompoundShapeURL() != currentCompoundShapeURL || !_model) {
EntityTreePointer tree = getTree();
@ -629,7 +638,7 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
}
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(url);
_compoundShapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
}
}
}
@ -661,7 +670,8 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
}
return true;
} else if (!getCompoundShapeURL().isEmpty()) {
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(getCompoundShapeURL());
_compoundShapeResource =
DependencyManager::get<ModelCache>()->getCollisionGeometryResource(getCompoundShapeURL());
}
}

View file

@ -302,7 +302,8 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
}
bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess) {
bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry,
float& scaleGuess, bool combineParts) {
FaceGroup faces;
FBXMesh& mesh = geometry.meshes[0];
mesh.parts.append(FBXMeshPart());
@ -348,6 +349,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
}
QByteArray groupName = tokenizer.getDatum();
currentGroup = groupName;
if (!combineParts) {
currentMaterialName = QString("part-") + QString::number(_partCounter++);
}
} else if (token == "mtllib" && !_url.isEmpty()) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
@ -361,7 +365,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
}
QString nextName = tokenizer.getDatum();
if (nextName != currentMaterialName) {
currentMaterialName = nextName;
if (combineParts) {
currentMaterialName = nextName;
}
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader new current material:" << currentMaterialName;
#endif
@ -419,7 +425,7 @@ done:
}
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url) {
PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr);
QBuffer buffer { &model };
buffer.open(QIODevice::ReadOnly);
@ -438,7 +444,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
try {
// call parseOBJGroup as long as it's returning true. Each successful call will
// add a new meshPart to the geometry's single mesh.
while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess)) {}
while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess, combineParts)) {}
FBXMesh& mesh = geometry.meshes[0];
mesh.meshIndex = 0;

View file

@ -22,7 +22,7 @@ public:
glm::vec3 getVec3();
glm::vec2 getVec2();
float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); }
private:
QIODevice* _device;
QByteArray _datum;
@ -73,15 +73,18 @@ public:
QHash<QString, OBJMaterial> materials;
QNetworkReply* request(QUrl& url, bool isTest);
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url = QUrl());
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl());
private:
QUrl _url;
QHash<QByteArray, bool> librariesSeen;
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess);
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry,
float& scaleGuess, bool combineParts);
void parseMaterialLibrary(QIODevice* device);
bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format.
int _partCounter { 0 };
};
// What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility.

View file

@ -32,6 +32,7 @@ class GeometryExtra {
public:
const QVariantHash& mapping;
const QUrl& textureBaseUrl;
bool combineParts;
};
QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
@ -89,7 +90,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
}
auto modelCache = DependencyManager::get<ModelCache>();
GeometryExtra extra{ mapping, _textureBaseUrl };
GeometryExtra extra{ mapping, _textureBaseUrl, false };
// Get the raw GeometryResource
_geometryResource = modelCache->getResource(url, QUrl(), &extra).staticCast<GeometryResource>();
@ -129,8 +130,8 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
class GeometryReader : public QRunnable {
public:
GeometryReader(QWeakPointer<Resource>& resource, const QUrl& url, const QVariantHash& mapping,
const QByteArray& data) :
_resource(resource), _url(url), _mapping(mapping), _data(data) {
const QByteArray& data, bool combineParts) :
_resource(resource), _url(url), _mapping(mapping), _data(data), _combineParts(combineParts) {
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
}
@ -142,6 +143,7 @@ private:
QUrl _url;
QVariantHash _mapping;
QByteArray _data;
bool _combineParts;
};
void GeometryReader::run() {
@ -178,7 +180,7 @@ void GeometryReader::run() {
throw QString("empty geometry, possibly due to an unsupported FBX version");
}
} else if (_url.path().toLower().endsWith(".obj")) {
fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _url));
fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url));
} else {
throw QString("unsupported format");
}
@ -209,8 +211,8 @@ void GeometryReader::run() {
class GeometryDefinitionResource : public GeometryResource {
Q_OBJECT
public:
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping) {}
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl, bool combineParts) :
GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping), _combineParts(combineParts) {}
QString getType() const override { return "GeometryDefinition"; }
@ -221,10 +223,11 @@ protected:
private:
QVariantHash _mapping;
bool _combineParts;
};
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data));
QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data, _combineParts));
}
void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) {
@ -265,7 +268,7 @@ ModelCache::ModelCache() {
}
QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) {
const void* extra) {
Resource* resource = nullptr;
if (url.path().toLower().endsWith(".fst")) {
resource = new GeometryMappingResource(url);
@ -273,14 +276,30 @@ QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QShar
const GeometryExtra* geometryExtra = static_cast<const GeometryExtra*>(extra);
auto mapping = geometryExtra ? geometryExtra->mapping : QVariantHash();
auto textureBaseUrl = geometryExtra ? geometryExtra->textureBaseUrl : QUrl();
resource = new GeometryDefinitionResource(url, mapping, textureBaseUrl);
bool combineParts = geometryExtra ? geometryExtra->combineParts : true;
resource = new GeometryDefinitionResource(url, mapping, textureBaseUrl, combineParts);
}
return QSharedPointer<Resource>(resource, &Resource::deleter);
}
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) {
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url,
const QVariantHash& mapping, const QUrl& textureBaseUrl) {
bool combineParts = true;
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast<GeometryResource>();
if (resource) {
if (resource->isLoaded() && resource->shouldSetTextures()) {
resource->setTextures();
}
}
return resource;
}
GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& url,
const QVariantHash& mapping, const QUrl& textureBaseUrl) {
bool combineParts = false;
GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts };
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast<GeometryResource>();
if (resource) {
if (resource->isLoaded() && resource->shouldSetTextures()) {

View file

@ -136,13 +136,18 @@ class ModelCache : public ResourceCache, public Dependency {
public:
GeometryResource::Pointer getGeometryResource(const QUrl& url,
const QVariantHash& mapping = QVariantHash(), const QUrl& textureBaseUrl = QUrl());
const QVariantHash& mapping = QVariantHash(),
const QUrl& textureBaseUrl = QUrl());
GeometryResource::Pointer getCollisionGeometryResource(const QUrl& url,
const QVariantHash& mapping = QVariantHash(),
const QUrl& textureBaseUrl = QUrl());
protected:
friend class GeometryMappingResource;
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) override;
const void* extra) override;
private:
ModelCache();

View file

@ -295,8 +295,8 @@ protected:
/// Creates a new resource.
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) = 0;
const void* extra) = 0;
void addUnusedResource(const QSharedPointer<Resource>& resource);
void removeUnusedResource(const QSharedPointer<Resource>& resource);

View file

@ -43,7 +43,8 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
QByteArray fbxContents = fbx.readAll();
FBXGeometry* geom;
if (filename.toLower().endsWith(".obj")) {
geom = OBJReader().readOBJ(fbxContents, QVariantHash());
bool combineParts = false;
geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts);
} else if (filename.toLower().endsWith(".fbx")) {
geom = readFBX(fbxContents, QVariantHash(), filename);
} else {

View file

@ -10,6 +10,7 @@
//
#include <QCommandLineParser>
#include <Trace.h>
#include <VHACD.h>
#include "VHACDUtilApp.h"
#include "VHACDUtil.h"
@ -98,6 +99,8 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
{
vhacd::VHACDUtil vUtil;
DependencyManager::set<tracing::Tracer>();
// parse command-line
QCommandLineParser parser;
parser.setApplicationDescription("High Fidelity Object Decomposer");