mirror of
https://github.com/lubosz/overte.git
synced 2025-06-03 04:50:35 +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
|
@ -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++) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue