mirror of
https://github.com/overte-org/overte.git
synced 2025-04-15 19:39:20 +02:00
Merge pull request #9811 from ZappoMan/triangleSet
Cleanup how Models do ray-picking and determination of point inside of convex hull
This commit is contained in:
commit
71c2b4215d
7 changed files with 249 additions and 241 deletions
|
@ -418,6 +418,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
// Enqueue updates for the next frame
|
||||
if (_model) {
|
||||
|
||||
#ifdef WANT_EXTRA_RENDER_DEBUGGING
|
||||
// debugging...
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
_model->renderDebugMeshBoxes(batch);
|
||||
#endif
|
||||
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
||||
|
|
|
@ -300,7 +300,7 @@ public:
|
|||
|
||||
QHash<QString, FBXMaterial> materials;
|
||||
|
||||
glm::mat4 offset;
|
||||
glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file
|
||||
|
||||
int leftEyeJointIndex = -1;
|
||||
int rightEyeJointIndex = -1;
|
||||
|
|
|
@ -96,9 +96,6 @@ Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestab
|
|||
_isVisible(true),
|
||||
_blendNumber(0),
|
||||
_appliedBlendNumber(0),
|
||||
_calculatedMeshPartBoxesValid(false),
|
||||
_calculatedMeshBoxesValid(false),
|
||||
_calculatedMeshTrianglesValid(false),
|
||||
_isWireframe(false),
|
||||
_rig(rig)
|
||||
{
|
||||
|
@ -360,53 +357,43 @@ 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
|
||||
// and testing intersection there.
|
||||
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
float distanceToSubMesh;
|
||||
BoxFace subMeshFace;
|
||||
glm::vec3 subMeshSurfaceNormal;
|
||||
int subMeshIndex = 0;
|
||||
|
||||
const FBXGeometry& geometry = getFBXGeometry();
|
||||
|
||||
// If we hit the models box, then consider the submeshes...
|
||||
_mutex.lock();
|
||||
if (!_calculatedMeshBoxesValid || (pickAgainstTriangles && !_calculatedMeshTrianglesValid)) {
|
||||
recalculateMeshBoxes(pickAgainstTriangles);
|
||||
if (!_triangleSetsValid) {
|
||||
calculateTriangleSets();
|
||||
}
|
||||
|
||||
for (const auto& subMeshBox : _calculatedMeshBoxes) {
|
||||
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
||||
glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix;
|
||||
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
|
||||
|
||||
if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace, subMeshSurfaceNormal)) {
|
||||
if (distanceToSubMesh < bestDistance) {
|
||||
if (pickAgainstTriangles) {
|
||||
// check our triangles here....
|
||||
const QVector<Triangle>& meshTriangles = _calculatedMeshTriangles[subMeshIndex];
|
||||
for(const auto& triangle : meshTriangles) {
|
||||
float thisTriangleDistance;
|
||||
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
|
||||
if (thisTriangleDistance < bestDistance) {
|
||||
bestDistance = thisTriangleDistance;
|
||||
intersectedSomething = true;
|
||||
face = subMeshFace;
|
||||
surfaceNormal = triangle.getNormal();
|
||||
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// this is the non-triangle picking case...
|
||||
bestDistance = distanceToSubMesh;
|
||||
intersectedSomething = true;
|
||||
face = subMeshFace;
|
||||
surfaceNormal = subMeshSurfaceNormal;
|
||||
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
|
||||
}
|
||||
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.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)) {
|
||||
|
||||
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
|
||||
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
|
||||
float worldDistance = glm::distance(origin, worldIntersectionPoint);
|
||||
|
||||
if (worldDistance < bestDistance) {
|
||||
bestDistance = worldDistance;
|
||||
intersectedSomething = true;
|
||||
face = triangleSetFace;
|
||||
surfaceNormal = glm::vec3(meshToWorldMatrix * glm::vec4(triangleSetNormal, 0.0f));
|
||||
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
|
||||
}
|
||||
}
|
||||
subMeshIndex++;
|
||||
}
|
||||
_mutex.unlock();
|
||||
|
||||
if (intersectedSomething) {
|
||||
distance = bestDistance;
|
||||
|
@ -442,172 +429,104 @@ bool Model::convexHullContains(glm::vec3 point) {
|
|||
// we can use the AABox's contains() by mapping our point into the model frame
|
||||
// and testing there.
|
||||
if (modelFrameBox.contains(modelFramePoint)){
|
||||
_mutex.lock();
|
||||
if (!_calculatedMeshTrianglesValid) {
|
||||
recalculateMeshBoxes(true);
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
if (!_triangleSetsValid) {
|
||||
calculateTriangleSets();
|
||||
}
|
||||
|
||||
// If we are inside the models box, then consider the submeshes...
|
||||
int subMeshIndex = 0;
|
||||
foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
|
||||
if (subMeshBox.contains(point)) {
|
||||
bool insideMesh = true;
|
||||
// To be inside the sub mesh, we need to be behind every triangles' planes
|
||||
const QVector<Triangle>& meshTriangles = _calculatedMeshTriangles[subMeshIndex];
|
||||
foreach (const Triangle& triangle, meshTriangles) {
|
||||
if (!isPointBehindTrianglesPlane(point, triangle.v0, triangle.v1, triangle.v2)) {
|
||||
// it's not behind at least one so we bail
|
||||
insideMesh = false;
|
||||
break;
|
||||
}
|
||||
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
||||
glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix;
|
||||
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
|
||||
glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
|
||||
|
||||
}
|
||||
if (insideMesh) {
|
||||
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
|
||||
const AABox& box = triangleSet.getBounds();
|
||||
if (box.contains(meshFramePoint)) {
|
||||
if (triangleSet.convexHullContains(meshFramePoint)) {
|
||||
// It's inside this mesh, return true.
|
||||
_mutex.unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
subMeshIndex++;
|
||||
}
|
||||
_mutex.unlock();
|
||||
|
||||
|
||||
}
|
||||
// It wasn't in any mesh, return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we seem to call this too often when things haven't actually changed... look into optimizing this
|
||||
// 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) {
|
||||
void Model::calculateTriangleSets() {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid;
|
||||
|
||||
if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) {
|
||||
const FBXGeometry& geometry = getFBXGeometry();
|
||||
int numberOfMeshes = geometry.meshes.size();
|
||||
_calculatedMeshBoxes.resize(numberOfMeshes);
|
||||
_calculatedMeshTriangles.clear();
|
||||
_calculatedMeshTriangles.resize(numberOfMeshes);
|
||||
_calculatedMeshPartBoxes.clear();
|
||||
for (int i = 0; i < numberOfMeshes; i++) {
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation);
|
||||
const FBXGeometry& geometry = getFBXGeometry();
|
||||
int numberOfMeshes = geometry.meshes.size();
|
||||
|
||||
_calculatedMeshBoxes[i] = AABox(scaledMeshExtents);
|
||||
_triangleSetsValid = true;
|
||||
_modelSpaceMeshTriangleSets.clear();
|
||||
_modelSpaceMeshTriangleSets.resize(numberOfMeshes);
|
||||
|
||||
if (pickAgainstTriangles) {
|
||||
QVector<Triangle> thisMeshTriangles;
|
||||
for (int j = 0; j < mesh.parts.size(); j++) {
|
||||
const FBXMeshPart& part = mesh.parts.at(j);
|
||||
for (int i = 0; i < numberOfMeshes; i++) {
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
|
||||
bool atLeastOnePointInBounds = false;
|
||||
AABox thisPartBounds;
|
||||
for (int j = 0; j < mesh.parts.size(); j++) {
|
||||
const FBXMeshPart& part = mesh.parts.at(j);
|
||||
|
||||
const int INDICES_PER_TRIANGLE = 3;
|
||||
const int INDICES_PER_QUAD = 4;
|
||||
const int INDICES_PER_TRIANGLE = 3;
|
||||
const int INDICES_PER_QUAD = 4;
|
||||
const int TRIANGLES_PER_QUAD = 2;
|
||||
|
||||
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++];
|
||||
// tell our triangleSet how many triangles to expect.
|
||||
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
|
||||
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
|
||||
int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris;
|
||||
_modelSpaceMeshTriangleSets[i].reserve(totalTriangles);
|
||||
|
||||
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));
|
||||
glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f));
|
||||
auto meshTransform = getFBXGeometry().offset * mesh.modelTransform;
|
||||
|
||||
// track the mesh parts in model space
|
||||
if (!atLeastOnePointInBounds) {
|
||||
thisPartBounds.setBox(mv0, 0.0f);
|
||||
atLeastOnePointInBounds = true;
|
||||
} else {
|
||||
thisPartBounds += mv0;
|
||||
}
|
||||
thisPartBounds += mv1;
|
||||
thisPartBounds += mv2;
|
||||
thisPartBounds += mv3;
|
||||
if (part.quadIndices.size() > 0) {
|
||||
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++];
|
||||
|
||||
glm::vec3 v0 = calculateScaledOffsetPoint(mv0);
|
||||
glm::vec3 v1 = calculateScaledOffsetPoint(mv1);
|
||||
glm::vec3 v2 = calculateScaledOffsetPoint(mv2);
|
||||
glm::vec3 v3 = calculateScaledOffsetPoint(mv3);
|
||||
// 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(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f));
|
||||
glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f));
|
||||
glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f));
|
||||
glm::vec3 v3 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i3], 1.0f));
|
||||
|
||||
// Sam's recommended triangle slices
|
||||
Triangle tri1 = { v0, v1, v3 };
|
||||
Triangle tri2 = { v1, v2, v3 };
|
||||
|
||||
// NOTE: Random guy on the internet's recommended triangle slices
|
||||
//Triangle tri1 = { v0, v1, v2 };
|
||||
//Triangle tri2 = { v2, v3, v0 };
|
||||
|
||||
thisMeshTriangles.push_back(tri1);
|
||||
thisMeshTriangles.push_back(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;
|
||||
|
||||
glm::vec3 v0 = calculateScaledOffsetPoint(mv0);
|
||||
glm::vec3 v1 = calculateScaledOffsetPoint(mv1);
|
||||
glm::vec3 v2 = calculateScaledOffsetPoint(mv2);
|
||||
|
||||
Triangle tri = { v0, v1, v2 };
|
||||
|
||||
thisMeshTriangles.push_back(tri);
|
||||
}
|
||||
}
|
||||
_calculatedMeshPartBoxes[QPair<int,int>(i, j)] = thisPartBounds;
|
||||
Triangle tri1 = { v0, v1, v3 };
|
||||
Triangle tri2 = { v1, v2, v3 };
|
||||
_modelSpaceMeshTriangleSets[i].insert(tri1);
|
||||
_modelSpaceMeshTriangleSets[i].insert(tri2);
|
||||
}
|
||||
}
|
||||
|
||||
if (part.triangleIndices.size() > 0) {
|
||||
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++];
|
||||
|
||||
// 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(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f));
|
||||
glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f));
|
||||
glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f));
|
||||
|
||||
Triangle tri = { v0, v1, v2 };
|
||||
_modelSpaceMeshTriangleSets[i].insert(tri);
|
||||
}
|
||||
_calculatedMeshTriangles[i] = thisMeshTriangles;
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -723,7 +642,17 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
|
|||
void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
|
||||
int colorNdx = 0;
|
||||
_mutex.lock();
|
||||
foreach(AABox box, _calculatedMeshBoxes) {
|
||||
|
||||
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
|
||||
glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix;
|
||||
Transform meshToWorld(meshToWorldMatrix);
|
||||
batch.setModelTransform(meshToWorld);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, true, true);
|
||||
|
||||
for(const auto& triangleSet : _modelSpaceMeshTriangleSets) {
|
||||
auto box = triangleSet.getBounds();
|
||||
|
||||
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
|
||||
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
@ -755,8 +684,8 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
|
|||
points << blf << tlf;
|
||||
|
||||
glm::vec4 color[] = {
|
||||
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
|
||||
{ 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
|
||||
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple
|
||||
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
|
||||
|
@ -814,37 +743,6 @@ Extents Model::getUnscaledMeshExtents() const {
|
|||
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) {
|
||||
_rig->clearJointState(index);
|
||||
}
|
||||
|
@ -1126,12 +1024,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
|
|||
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
|
||||
|
||||
if (isActive() && fullUpdate) {
|
||||
// NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid
|
||||
// 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
|
||||
// because ray picking needs valid boxes to work
|
||||
_calculatedMeshBoxesValid = false;
|
||||
_calculatedMeshTrianglesValid = false;
|
||||
onInvalidate();
|
||||
|
||||
// check for scale to fit
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <render/Scene.h>
|
||||
#include <Transform.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
#include <TriangleSet.h>
|
||||
|
||||
#include "GeometryCache.h"
|
||||
#include "TextureCache.h"
|
||||
|
@ -95,7 +96,6 @@ public:
|
|||
render::PendingChanges& pendingChanges,
|
||||
render::Item::Status::Getters& statusGetters);
|
||||
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
void renderSetup(RenderArgs* args);
|
||||
bool isRenderable() const;
|
||||
|
||||
bool isVisible() const { return _isVisible; }
|
||||
|
@ -250,6 +250,9 @@ public:
|
|||
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
|
||||
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItems; }
|
||||
|
||||
void renderDebugMeshBoxes(gpu::Batch& batch);
|
||||
|
||||
|
||||
public slots:
|
||||
void loadURLFinished(bool success);
|
||||
|
||||
|
@ -266,15 +269,6 @@ protected:
|
|||
/// Returns the unscaled extents of the model's mesh
|
||||
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
|
||||
void clearJointState(int index);
|
||||
|
||||
|
@ -293,9 +287,13 @@ protected:
|
|||
|
||||
SpatiallyNestable* _spatiallyNestableOverride;
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::vec3 _translation; // this is the translation in world coordinates to the model's registration point
|
||||
glm::quat _rotation;
|
||||
glm::vec3 _scale;
|
||||
|
||||
// For entity models this is the translation for the minimum extent of the model (in original mesh coordinate space)
|
||||
// to the model's registration point. For avatar models this is the translation from the avatar's hips, as determined
|
||||
// by the default pose, to the origin.
|
||||
glm::vec3 _offset;
|
||||
|
||||
static float FAKE_DIMENSION_PLACEHOLDER;
|
||||
|
@ -331,14 +329,13 @@ protected:
|
|||
|
||||
/// Allow sub classes to force invalidating the bboxes
|
||||
void invalidCalculatedMeshBoxes() {
|
||||
_calculatedMeshBoxesValid = false;
|
||||
_calculatedMeshPartBoxesValid = false;
|
||||
_calculatedMeshTrianglesValid = false;
|
||||
_triangleSetsValid = false;
|
||||
}
|
||||
|
||||
// hook for derived classes to be notified when setUrl invalidates the current model.
|
||||
virtual void onInvalidate() {};
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual void deleteGeometry();
|
||||
|
@ -357,17 +354,12 @@ protected:
|
|||
int _blendNumber;
|
||||
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< QVector<Triangle> > _calculatedMeshTriangles; // world coordinate triangles for all sub meshes
|
||||
bool _calculatedMeshTrianglesValid;
|
||||
QMutex _mutex;
|
||||
|
||||
void recalculateMeshBoxes(bool pickAgainstTriangles = false);
|
||||
bool _triangleSetsValid { false };
|
||||
void calculateTriangleSets();
|
||||
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
|
||||
|
||||
|
||||
void createRenderItemSet();
|
||||
virtual void createVisibleRenderItemSet();
|
||||
|
@ -376,7 +368,6 @@ protected:
|
|||
bool _isWireframe;
|
||||
|
||||
// debug rendering support
|
||||
void renderDebugMeshBoxes(gpu::Batch& batch);
|
||||
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
|
||||
|
||||
|
||||
|
|
|
@ -109,6 +109,8 @@ public:
|
|||
|
||||
bool isInvalid() const { return _corner == INFINITY_VECTOR; }
|
||||
|
||||
void clear() { _corner = INFINITY_VECTOR; _scale = glm::vec3(0.0f); }
|
||||
|
||||
private:
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||
|
|
76
libraries/shared/src/TriangleSet.cpp
Normal file
76
libraries/shared/src/TriangleSet.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// TriangleSet.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 3/2/2017.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "GLMHelpers.h"
|
||||
#include "TriangleSet.h"
|
||||
|
||||
void TriangleSet::insert(const Triangle& t) {
|
||||
_triangles.push_back(t);
|
||||
|
||||
_bounds += t.v0;
|
||||
_bounds += t.v1;
|
||||
_bounds += t.v2;
|
||||
}
|
||||
|
||||
void TriangleSet::clear() {
|
||||
_triangles.clear();
|
||||
_bounds.clear();
|
||||
}
|
||||
|
||||
// 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.
|
||||
bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const {
|
||||
|
||||
bool intersectedSomething = false;
|
||||
float boxDistance = std::numeric_limits<float>::max();
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) {
|
||||
if (precision) {
|
||||
for (const auto& triangle : _triangles) {
|
||||
float thisTriangleDistance;
|
||||
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
|
||||
if (thisTriangleDistance < bestDistance) {
|
||||
bestDistance = thisTriangleDistance;
|
||||
intersectedSomething = true;
|
||||
surfaceNormal = triangle.getNormal();
|
||||
distance = bestDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
intersectedSomething = true;
|
||||
distance = boxDistance;
|
||||
}
|
||||
}
|
||||
|
||||
return intersectedSomething;
|
||||
}
|
||||
|
||||
|
||||
bool TriangleSet::convexHullContains(const glm::vec3& point) const {
|
||||
if (!_bounds.contains(point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool insideMesh = true; // optimistic
|
||||
for (const auto& triangle : _triangles) {
|
||||
if (!isPointBehindTrianglesPlane(point, triangle.v0, triangle.v1, triangle.v2)) {
|
||||
// it's not behind at least one so we bail
|
||||
insideMesh = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return insideMesh;
|
||||
}
|
||||
|
41
libraries/shared/src/TriangleSet.h
Normal file
41
libraries/shared/src/TriangleSet.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// TriangleSet.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 3/2/2017.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "AABox.h"
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
class TriangleSet {
|
||||
public:
|
||||
void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles
|
||||
size_t size() const { return _triangles.size(); }
|
||||
|
||||
const Triangle& getTriangle(size_t t) const { return _triangles[t]; }
|
||||
|
||||
void insert(const Triangle& t);
|
||||
void clear();
|
||||
|
||||
// Determine if 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.
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const;
|
||||
|
||||
// Determine if a point is "inside" all the triangles of a convex hull. It is the responsibility of the caller to
|
||||
// determine that the triangle set is indeed a convex hull. If the triangles added to this set are not in fact a
|
||||
// convex hull, the result of this method is meaningless and undetermined.
|
||||
bool convexHullContains(const glm::vec3& point) const;
|
||||
const AABox& getBounds() const { return _bounds; }
|
||||
|
||||
private:
|
||||
std::vector<Triangle> _triangles;
|
||||
AABox _bounds;
|
||||
};
|
Loading…
Reference in a new issue