cleanup use of triangleSet for picking

This commit is contained in:
ZappoMan 2017-03-05 19:23:55 -08:00
parent 32add6635d
commit 87bcced409
5 changed files with 134 additions and 272 deletions

View file

@ -418,6 +418,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
// Enqueue updates for the next frame // Enqueue updates for the next frame
if (_model) { if (_model) {
#if 1 //def WANT_EXTRA_RENDER_DEBUGGING
// debugging...
gpu::Batch& batch = *args->_batch;
_model->renderDebugMeshBoxes(batch);
#endif
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
// FIXME: this seems like it could be optimized if we tracked our last known visible state in // FIXME: this seems like it could be optimized if we tracked our last known visible state in

View file

@ -96,9 +96,6 @@ Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestab
_isVisible(true), _isVisible(true),
_blendNumber(0), _blendNumber(0),
_appliedBlendNumber(0), _appliedBlendNumber(0),
_calculatedMeshPartBoxesValid(false),
_calculatedMeshBoxesValid(false),
_calculatedMeshTrianglesValid(false),
_isWireframe(false), _isWireframe(false),
_rig(rig) _rig(rig)
{ {
@ -360,19 +357,14 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame // we can use the AABox's ray intersection by mapping our origin and direction into the model frame
// and testing intersection there. // and testing intersection there.
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) {
QMutexLocker locker(&_mutex);
float bestDistance = std::numeric_limits<float>::max(); float bestDistance = std::numeric_limits<float>::max();
float distanceToSubMesh;
BoxFace subMeshFace;
glm::vec3 subMeshSurfaceNormal;
int subMeshIndex = 0; int subMeshIndex = 0;
const FBXGeometry& geometry = getFBXGeometry(); const FBXGeometry& geometry = getFBXGeometry();
// If we hit the models box, then consider the submeshes... if (!_triangleSetsValid) {
_mutex.lock(); calculateTriangleSets();
if (!_calculatedMeshBoxesValid || (pickAgainstTriangles && !_calculatedMeshTrianglesValid)) {
recalculateMeshBoxes(pickAgainstTriangles);
} }
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
@ -382,50 +374,26 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f)); glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
float triangleSetDistance = 0.0f;
BoxFace triangleSetFace;
glm::vec3 triangleSetNormal;
if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetNormal, pickAgainstTriangles)) {
for (const auto& subMeshBox : _calculatedMeshBoxes) { glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
bool intersectedSubMesh = false; glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
float subMeshDistance = std::numeric_limits<float>::max(); float worldDistance = glm::distance(origin, worldIntersectionPoint);
if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace, subMeshSurfaceNormal)) { if (worldDistance < bestDistance) {
if (distanceToSubMesh < bestDistance) { bestDistance = worldDistance;
if (pickAgainstTriangles) { intersectedSomething = true;
face = triangleSetFace;
float subMeshDistance = 0.0f; surfaceNormal = glm::vec3(meshToWorldMatrix * glm::vec4(triangleSetNormal, 0.0f));
glm::vec3 subMeshNormal; extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
const auto& meshTriangleSet = _modelSpaceMeshTriangleSets[subMeshIndex];
bool intersectedMesh = meshTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, subMeshDistance, subMeshNormal);
if (intersectedMesh) {
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * subMeshDistance);
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
float worldDistance = glm::distance(origin, worldIntersectionPoint);
if (worldDistance < bestDistance) {
bestDistance = subMeshDistance;
intersectedSomething = true;
face = subMeshFace;
surfaceNormal = glm::vec3(meshToWorldMatrix * glm::vec4(subMeshNormal, 0.0f));
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
}
}
} else {
// this is the non-triangle picking case...
bestDistance = distanceToSubMesh;
intersectedSomething = true;
face = subMeshFace;
surfaceNormal = subMeshSurfaceNormal;
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
intersectedSubMesh = true;
}
} }
} }
subMeshIndex++; subMeshIndex++;
} }
_mutex.unlock();
if (intersectedSomething) { if (intersectedSomething) {
distance = bestDistance; distance = bestDistance;
@ -461,182 +429,97 @@ bool Model::convexHullContains(glm::vec3 point) {
// we can use the AABox's contains() by mapping our point into the model frame // we can use the AABox's contains() by mapping our point into the model frame
// and testing there. // and testing there.
if (modelFrameBox.contains(modelFramePoint)){ if (modelFrameBox.contains(modelFramePoint)){
_mutex.lock(); QMutexLocker locker(&_mutex);
if (!_calculatedMeshTrianglesValid) {
recalculateMeshBoxes(true); if (!_triangleSetsValid) {
calculateTriangleSets();
} }
// If we are inside the models box, then consider the submeshes... // If we are inside the models box, then consider the submeshes...
int subMeshIndex = 0; glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
foreach(const AABox& subMeshBox, _calculatedMeshBoxes) { glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
if (subMeshBox.contains(point)) { glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); const AABox& box = triangleSet.getBounds();
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); if (box.contains(meshFramePoint)) {
if (triangleSet.convexHullContains(meshFramePoint)) {
glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
if (_modelSpaceMeshTriangleSets[subMeshIndex].convexHullContains(meshFramePoint)) {
// It's inside this mesh, return true. // It's inside this mesh, return true.
_mutex.unlock();
return true; return true;
} }
} }
subMeshIndex++;
} }
_mutex.unlock();
} }
// It wasn't in any mesh, return false. // It wasn't in any mesh, return false.
return false; return false;
} }
// TODO: we seem to call this too often when things haven't actually changed... look into optimizing this void Model::calculateTriangleSets() {
// Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these
// can occur multiple times. In addition, rendering does it's own ray picking in order to decide which
// entity-scripts to call. I think it would be best to do the picking once-per-frame (in cpu, or gpu if possible)
// and then the calls use the most recent such result.
void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
PROFILE_RANGE(render, __FUNCTION__); PROFILE_RANGE(render, __FUNCTION__);
bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid;
if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) { const FBXGeometry& geometry = getFBXGeometry();
int numberOfMeshes = geometry.meshes.size();
if (pickAgainstTriangles) { _triangleSetsValid = true;
qDebug() << "RECALCULATING triangles!"; _modelSpaceMeshTriangleSets.clear();
} else { _modelSpaceMeshTriangleSets.resize(numberOfMeshes);
qDebug() << "RECALCULATING boxes!";
}
const FBXGeometry& geometry = getFBXGeometry(); for (int i = 0; i < numberOfMeshes; i++) {
int numberOfMeshes = geometry.meshes.size(); const FBXMesh& mesh = geometry.meshes.at(i);
_calculatedMeshBoxes.resize(numberOfMeshes);
_calculatedMeshPartBoxes.clear();
_modelSpaceMeshTriangleSets.clear(); for (int j = 0; j < mesh.parts.size(); j++) {
_modelSpaceMeshTriangleSets.resize(numberOfMeshes); const FBXMeshPart& part = mesh.parts.at(j);
for (int i = 0; i < numberOfMeshes; i++) { const int INDICES_PER_TRIANGLE = 3;
const FBXMesh& mesh = geometry.meshes.at(i); const int INDICES_PER_QUAD = 4;
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation);
_calculatedMeshBoxes[i] = AABox(scaledMeshExtents); if (part.quadIndices.size() > 0) {
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
int vIndex = 0;
for (int q = 0; q < numberOfQuads; q++) {
int i0 = part.quadIndices[vIndex++];
int i1 = part.quadIndices[vIndex++];
int i2 = part.quadIndices[vIndex++];
int i3 = part.quadIndices[vIndex++];
if (pickAgainstTriangles) { // track the model space version... these points will be transformed by the FST's offset,
// which includes the scaling, rotation, and translation specified by the FST/FBX,
// this can't change at runtime, so we can safely store these in our TriangleSet
glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
glm::vec3 v3 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i3], 1.0f));
for (int j = 0; j < mesh.parts.size(); j++) { Triangle tri1 = { v0, v1, v3 };
const FBXMeshPart& part = mesh.parts.at(j); Triangle tri2 = { v1, v2, v3 };
_modelSpaceMeshTriangleSets[i].insertTriangle(tri1);
bool atLeastOnePointInBounds = false; _modelSpaceMeshTriangleSets[i].insertTriangle(tri2);
AABox thisPartBounds; }
}
const int INDICES_PER_TRIANGLE = 3;
const int INDICES_PER_QUAD = 4; if (part.triangleIndices.size() > 0) {
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
if (part.quadIndices.size() > 0) { int vIndex = 0;
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD; for (int t = 0; t < numberOfTris; t++) {
int vIndex = 0; int i0 = part.triangleIndices[vIndex++];
for (int q = 0; q < numberOfQuads; q++) { int i1 = part.triangleIndices[vIndex++];
int i0 = part.quadIndices[vIndex++]; int i2 = part.triangleIndices[vIndex++];
int i1 = part.quadIndices[vIndex++];
int i2 = part.quadIndices[vIndex++]; // track the model space version... these points will be transformed by the FST's offset,
int i3 = part.quadIndices[vIndex++]; // which includes the scaling, rotation, and translation specified by the FST/FBX,
// this can't change at runtime, so we can safely store these in our TriangleSet
glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f)); glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f)); glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f)); glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f));
Triangle tri = { v0, v1, v2 };
// track the mesh parts in model space _modelSpaceMeshTriangleSets[i].insertTriangle(tri);
if (!atLeastOnePointInBounds) {
thisPartBounds.setBox(mv0, 0.0f);
atLeastOnePointInBounds = true;
} else {
thisPartBounds += mv0;
}
thisPartBounds += mv1;
thisPartBounds += mv2;
thisPartBounds += mv3;
// let's also track the model space version... (eventually using only this!)
// these points will be transformed by the FST's offset, which includes the
// scaling, rotation, and translation specified by the FST/FBX, this can't change
// at runtime, so we can safely store these in our TriangleSet
{
glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
glm::vec3 v3 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i3], 1.0f));
Triangle tri1 = { v0, v1, v3 };
Triangle tri2 = { v1, v2, v3 };
_modelSpaceMeshTriangleSets[i].insertTriangle(tri1);
_modelSpaceMeshTriangleSets[i].insertTriangle(tri2);
}
}
}
if (part.triangleIndices.size() > 0) {
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
int vIndex = 0;
for (int t = 0; t < numberOfTris; t++) {
int i0 = part.triangleIndices[vIndex++];
int i1 = part.triangleIndices[vIndex++];
int i2 = part.triangleIndices[vIndex++];
glm::vec3 mv0 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i0], 1.0f));
glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f));
// track the mesh parts in model space
if (!atLeastOnePointInBounds) {
thisPartBounds.setBox(mv0, 0.0f);
atLeastOnePointInBounds = true;
} else {
thisPartBounds += mv0;
}
thisPartBounds += mv1;
thisPartBounds += mv2;
// let's also track the model space version... (eventually using only this!)
// these points will be transformed by the FST's offset, which includes the
// scaling, rotation, and translation specified by the FST/FBX, this can't change
// at runtime, so we can safely store these in our TriangleSet
{
glm::vec3 v0 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i0], 1.0f));
glm::vec3 v1 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f));
Triangle tri = { v0, v1, v2 };
_modelSpaceMeshTriangleSets[i].insertTriangle(tri);
}
}
}
_calculatedMeshPartBoxes[QPair<int,int>(i, j)] = thisPartBounds;
} }
_calculatedMeshPartBoxesValid = true;
} }
} }
_calculatedMeshBoxesValid = true;
_calculatedMeshTrianglesValid = pickAgainstTriangles;
}
}
void Model::renderSetup(RenderArgs* args) {
// set up dilated textures on first render after load/simulate
const FBXGeometry& geometry = getFBXGeometry();
if (_dilatedTextures.isEmpty()) {
foreach (const FBXMesh& mesh, geometry.meshes) {
QVector<QSharedPointer<Texture> > dilated;
dilated.resize(mesh.parts.size());
_dilatedTextures.append(dilated);
}
}
if (!_addedToScene && isLoaded()) {
createRenderItemSet();
} }
} }
@ -752,7 +635,15 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
void Model::renderDebugMeshBoxes(gpu::Batch& batch) { void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
int colorNdx = 0; int colorNdx = 0;
_mutex.lock(); _mutex.lock();
foreach(AABox box, _calculatedMeshBoxes) {
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation);
Transform meshToWorld(meshToWorldMatrix);
batch.setModelTransform(meshToWorld);
for(const auto& triangleSet : _modelSpaceMeshTriangleSets) {
auto box = triangleSet.getBounds();
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) { if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID(); _debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
} }
@ -784,8 +675,8 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
points << blf << tlf; points << blf << tlf;
glm::vec4 color[] = { glm::vec4 color[] = {
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 1.0f, 0.0f, 1.0f }, // green { 0.0f, 1.0f, 0.0f, 1.0f }, // green
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 0.0f, 1.0f, 1.0f }, // blue { 0.0f, 0.0f, 1.0f, 1.0f }, // blue
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple { 1.0f, 0.0f, 1.0f, 1.0f }, // purple
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow { 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
@ -843,37 +734,6 @@ Extents Model::getUnscaledMeshExtents() const {
return scaledExtents; return scaledExtents;
} }
Extents Model::calculateScaledOffsetExtents(const Extents& extents,
glm::vec3 modelPosition, glm::quat modelOrientation) const {
// we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
Extents scaledOffsetExtents = { ((minimum + _offset) * _scale),
((maximum + _offset) * _scale) };
Extents rotatedExtents = scaledOffsetExtents.getRotated(modelOrientation);
Extents translatedExtents = { rotatedExtents.minimum + modelPosition,
rotatedExtents.maximum + modelPosition };
return translatedExtents;
}
/// Returns the world space equivalent of some box in model space.
AABox Model::calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const {
return AABox(calculateScaledOffsetExtents(Extents(box), modelPosition, modelOrientation));
}
glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const {
// we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
glm::vec3 offsetPoint = glm::vec3(getFBXGeometry().offset * glm::vec4(point, 1.0f));
glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale);
glm::vec3 rotatedPoint = _rotation * scaledPoint;
glm::vec3 translatedPoint = rotatedPoint + _translation;
return translatedPoint;
}
void Model::clearJointState(int index) { void Model::clearJointState(int index) {
_rig->clearJointState(index); _rig->clearJointState(index);
} }
@ -1159,8 +1019,11 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
// they really only become invalid if something about the transform to world space has changed. This is // they really only become invalid if something about the transform to world space has changed. This is
// not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking // not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking
// because ray picking needs valid boxes to work // because ray picking needs valid boxes to work
_calculatedMeshBoxesValid = false; //_calculatedMeshBoxesValid = false;
_calculatedMeshTrianglesValid = false; //_calculatedMeshTrianglesValid = false;
// FIXME -- if the model URL changes, then we need to recalculate the triangle sets??!!!!
onInvalidate(); onInvalidate();
// check for scale to fit // check for scale to fit

View file

@ -96,7 +96,6 @@ public:
render::PendingChanges& pendingChanges, render::PendingChanges& pendingChanges,
render::Item::Status::Getters& statusGetters); render::Item::Status::Getters& statusGetters);
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges); void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
void renderSetup(RenderArgs* args);
bool isRenderable() const; bool isRenderable() const;
bool isVisible() const { return _isVisible; } bool isVisible() const { return _isVisible; }
@ -251,6 +250,9 @@ public:
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItems; } const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItems; }
void renderDebugMeshBoxes(gpu::Batch& batch);
public slots: public slots:
void loadURLFinished(bool success); void loadURLFinished(bool success);
@ -267,15 +269,6 @@ protected:
/// Returns the unscaled extents of the model's mesh /// Returns the unscaled extents of the model's mesh
Extents getUnscaledMeshExtents() const; Extents getUnscaledMeshExtents() const;
/// Returns the scaled equivalent of some extents in model space.
Extents calculateScaledOffsetExtents(const Extents& extents, glm::vec3 modelPosition, glm::quat modelOrientation) const;
/// Returns the world space equivalent of some box in model space.
AABox calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPosition, glm::quat modelOrientation) const;
/// Returns the scaled equivalent of a point in model space.
glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const;
/// Clear the joint states /// Clear the joint states
void clearJointState(int index); void clearJointState(int index);
@ -332,14 +325,13 @@ protected:
/// Allow sub classes to force invalidating the bboxes /// Allow sub classes to force invalidating the bboxes
void invalidCalculatedMeshBoxes() { void invalidCalculatedMeshBoxes() {
_calculatedMeshBoxesValid = false; _triangleSetsValid = false;
_calculatedMeshPartBoxesValid = false;
_calculatedMeshTrianglesValid = false;
} }
// hook for derived classes to be notified when setUrl invalidates the current model. // hook for derived classes to be notified when setUrl invalidates the current model.
virtual void onInvalidate() {}; virtual void onInvalidate() {};
protected: protected:
virtual void deleteGeometry(); virtual void deleteGeometry();
@ -358,17 +350,12 @@ protected:
int _blendNumber; int _blendNumber;
int _appliedBlendNumber; int _appliedBlendNumber;
QHash<QPair<int,int>, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes
bool _calculatedMeshPartBoxesValid;
QVector<AABox> _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes
bool _calculatedMeshBoxesValid;
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
bool _calculatedMeshTrianglesValid;
QMutex _mutex; QMutex _mutex;
void recalculateMeshBoxes(bool pickAgainstTriangles = false); bool _triangleSetsValid { false };
void calculateTriangleSets();
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
void createRenderItemSet(); void createRenderItemSet();
virtual void createVisibleRenderItemSet(); virtual void createVisibleRenderItemSet();
@ -377,7 +364,6 @@ protected:
bool _isWireframe; bool _isWireframe;
// debug rendering support // debug rendering support
void renderDebugMeshBoxes(gpu::Batch& batch);
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;

View file

@ -28,24 +28,31 @@ void TriangleSet::clear() {
// Determine of the given ray (origin/direction) in model space intersects with any triangles // Determine of the given ray (origin/direction) in model space intersects with any triangles
// in the set. If an intersection occurs, the distance and surface normal will be provided. // in the set. If an intersection occurs, the distance and surface normal will be provided.
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, glm::vec3& surfaceNormal) const { float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const {
float bestDistance = std::numeric_limits<float>::max();
float thisTriangleDistance;
bool intersectedSomething = false; bool intersectedSomething = false;
float boxDistance = std::numeric_limits<float>::max();
float bestDistance = std::numeric_limits<float>::max();
for (const auto& triangle : _triangles) { if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) {
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { if (precision) {
if (thisTriangleDistance < bestDistance) { for (const auto& triangle : _triangles) {
bestDistance = thisTriangleDistance; float thisTriangleDistance;
intersectedSomething = true; if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
surfaceNormal = triangle.getNormal(); if (thisTriangleDistance < bestDistance) {
distance = bestDistance; bestDistance = thisTriangleDistance;
//face = subMeshFace; intersectedSomething = true;
//extraInfo = geometry.getModelNameOfMesh(subMeshIndex); surfaceNormal = triangle.getNormal();
distance = bestDistance;
}
}
} }
} else {
intersectedSomething = true;
distance = boxDistance;
} }
} }
return intersectedSomething; return intersectedSomething;
} }

View file

@ -22,10 +22,10 @@ public:
// Determine of the given ray (origin/direction) in model space intersects with any triangles // Determine of the given ray (origin/direction) in model space intersects with any triangles
// in the set. If an intersection occurs, the distance and surface normal will be provided. // in the set. If an intersection occurs, the distance and surface normal will be provided.
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, glm::vec3& surfaceNormal) const; float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const;
bool convexHullContains(const glm::vec3& point) const; // this point is "inside" all triangles bool convexHullContains(const glm::vec3& point) const; // this point is "inside" all triangles
const AABox& getBounds() const; const AABox& getBounds() const { return _bounds; }
private: private:
QVector<Triangle> _triangles; QVector<Triangle> _triangles;