mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 09:35:31 +02:00
Merge pull request #4552 from sethalves/island
Fix various collision-model related bugs
This commit is contained in:
commit
d77333953e
5 changed files with 72 additions and 55 deletions
libraries
entities-renderer/src
fbx/src
networking/src
render-utils/src
shared/src
|
@ -61,7 +61,7 @@ void RenderableModelEntityItem::remapTextures() {
|
|||
}
|
||||
|
||||
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()) {
|
||||
|
@ -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 (!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.
|
||||
if (_model && QUrl(getModelURL()) != _model->getURL()) {
|
||||
result = _model = _myRenderer->updateModel(_model, getModelURL(), getCollisionModelURL());
|
||||
|
@ -293,7 +293,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -312,35 +312,30 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
info.setParams(getShapeType(), 0.5f * getDimensions());
|
||||
} else {
|
||||
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();
|
||||
unsigned int 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.
|
||||
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
|
||||
foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
|
||||
// each meshPart is a convex hull
|
||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||
QVector<glm::vec3> pointsInPart;
|
||||
|
||||
// run through all the triangles and (uniquely) add each point to the hull
|
||||
unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
|
||||
assert((unsigned int)meshPart.triangleIndices.size() == triangleCount*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];
|
||||
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 p1 = mesh.vertices[p1Index];
|
||||
glm::vec3 p2 = mesh.vertices[p2Index];
|
||||
aaBox += p0;
|
||||
aaBox += p1;
|
||||
aaBox += p2;
|
||||
if (!pointsInPart.contains(p0)) {
|
||||
pointsInPart << p0;
|
||||
}
|
||||
|
@ -360,18 +355,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
unsigned int p1Index = meshPart.quadIndices[j*4+1];
|
||||
unsigned int p2Index = meshPart.quadIndices[j*4+2];
|
||||
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 p1 = mesh.vertices[p1Index];
|
||||
glm::vec3 p2 = mesh.vertices[p2Index];
|
||||
glm::vec3 p3 = mesh.vertices[p3Index];
|
||||
aaBox += p0;
|
||||
aaBox += p1;
|
||||
aaBox += p2;
|
||||
aaBox += p3;
|
||||
if (!pointsInPart.contains(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
|
||||
QVector<glm::vec3> newMeshPoints;
|
||||
_points << newMeshPoints;
|
||||
|
@ -394,11 +386,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
}
|
||||
}
|
||||
|
||||
// make sure we aren't about to divide by zero
|
||||
glm::vec3 aaBoxDim = aaBox.getDimensions();
|
||||
aaBoxDim = glm::clamp(aaBoxDim, glm::vec3(FLT_EPSILON), aaBoxDim);
|
||||
// We expect that the collision model will have the same units and will be displaced
|
||||
// from its origin in the same way the visual model is. The visual model has
|
||||
// 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
|
||||
for (int i = 0; i < _points.size(); i++) {
|
||||
for (int j = 0; j < _points[i].size(); j++) {
|
||||
|
|
|
@ -27,7 +27,8 @@ public:
|
|||
enum SpecialToken {
|
||||
NO_TOKEN = -1,
|
||||
NO_PUSHBACKED_TOKEN = -1,
|
||||
DATUM_TOKEN = 0x100
|
||||
DATUM_TOKEN = 0x100,
|
||||
COMMENT_TOKEN = 0x101
|
||||
};
|
||||
int nextToken();
|
||||
const QByteArray& getDatum() const { return _datum; }
|
||||
|
@ -35,11 +36,13 @@ public:
|
|||
void skipLine() { _device->readLine(); }
|
||||
void pushBackToken(int token) { _pushedBackToken = token; }
|
||||
void ungetChar(char ch) { _device->ungetChar(ch); }
|
||||
const QString getComment() const { return _comment; }
|
||||
|
||||
private:
|
||||
QIODevice* _device;
|
||||
QByteArray _datum;
|
||||
int _pushedBackToken;
|
||||
QString _comment;
|
||||
};
|
||||
|
||||
|
||||
|
@ -56,9 +59,11 @@ int OBJTokenizer::nextToken() {
|
|||
continue; // skip whitespace
|
||||
}
|
||||
switch (ch) {
|
||||
case '#':
|
||||
_device->readLine(); // skip the comment
|
||||
break;
|
||||
case '#': {
|
||||
_comment = _device->readLine(); // skip the comment
|
||||
qDebug() << "COMMENT:" << _comment;
|
||||
return COMMENT_TOKEN;
|
||||
}
|
||||
|
||||
case '\"':
|
||||
_datum = "";
|
||||
|
@ -104,7 +109,8 @@ bool OBJTokenizer::isNextTokenFloat() {
|
|||
}
|
||||
|
||||
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];
|
||||
mesh.parts.append(FBXMeshPart());
|
||||
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));
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
@ -192,6 +208,7 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
|
|||
while (true) {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
if (indices.count() == 0) {
|
||||
// nonsense, bail out.
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
|
@ -266,22 +283,22 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
|
|||
}
|
||||
} else {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
if (meshPart.triangleIndices.size() == 0 && meshPart.quadIndices.size() == 0) {
|
||||
// empty mesh?
|
||||
mesh.parts.pop_back();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
FBXGeometry extractOBJGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
||||
FBXGeometry geometry;
|
||||
return geometry;
|
||||
}
|
||||
|
||||
|
||||
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) {
|
||||
QBuffer buffer(const_cast<QByteArray*>(&model));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
@ -294,24 +311,30 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
|
|||
OBJTokenizer tokenizer(device);
|
||||
QVector<int> faceNormalIndexes;
|
||||
QVector<glm::vec3> faceNormals;
|
||||
float scaleGuess = 1.0f;
|
||||
|
||||
faceNormalIndexes.clear();
|
||||
|
||||
geometry.meshExtents.reset();
|
||||
geometry.meshes.append(FBXMesh());
|
||||
|
||||
|
||||
|
||||
try {
|
||||
// call parseOBJGroup as long as it's returning true. Each successful call will
|
||||
// add a new meshPart to the geometry's single mesh.
|
||||
bool success = true;
|
||||
while (success) {
|
||||
success = parseOBJGroup(tokenizer, mapping, geometry, faceNormals, faceNormalIndexes);
|
||||
success = parseOBJGroup(tokenizer, mapping, geometry, faceNormals, faceNormalIndexes, scaleGuess);
|
||||
}
|
||||
|
||||
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();
|
||||
foreach (const glm::vec3& vertex, mesh.vertices) {
|
||||
mesh.meshExtents.addPoint(vertex);
|
||||
|
|
|
@ -70,6 +70,7 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
|
|||
bool delayLoad, void* extra) {
|
||||
QSharedPointer<Resource> resource = _resources.value(url);
|
||||
if (!resource.isNull()) {
|
||||
removeUnusedResource(resource);
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
@ -83,16 +84,14 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
|
|||
return getResource(fallback, QUrl(), delayLoad);
|
||||
}
|
||||
|
||||
if (resource.isNull()) {
|
||||
resource = createResource(url, fallback.isValid() ?
|
||||
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
|
||||
resource->setSelf(resource);
|
||||
resource->setCache(this);
|
||||
_resources.insert(url, resource);
|
||||
|
||||
} else {
|
||||
removeUnusedResource(resource);
|
||||
}
|
||||
resource = createResource(url, fallback.isValid() ?
|
||||
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
|
||||
resource->setSelf(resource);
|
||||
resource->setCache(this);
|
||||
_resources.insert(url, resource);
|
||||
removeUnusedResource(resource);
|
||||
resource->ensureLoading();
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
|
|
@ -1775,10 +1775,10 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
|
|||
return getResource(url, fallback, delayLoad, NULL).staticCast<NetworkGeometry>();
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
|
||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
bool delayLoad, const void* extra) {
|
||||
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad),
|
||||
&Resource::allReferencesCleared);
|
||||
&Resource::allReferencesCleared);
|
||||
geometry->setLODParent(geometry);
|
||||
return geometry.staticCast<Resource>();
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
void rotate(const glm::quat& rotation);
|
||||
|
||||
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
|
||||
Extents getRotated(const glm::quat& rotation) const {
|
||||
|
|
Loading…
Reference in a new issue