From 9908723bb9192da863862671d474630d02d7e952 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 3 Mar 2017 22:27:38 -0800 Subject: [PATCH 1/7] fist cut at basic TriangleSet class --- libraries/render-utils/src/Model.cpp | 65 +++++++++++++++----------- libraries/render-utils/src/Model.h | 3 +- libraries/shared/src/AABox.h | 2 + libraries/shared/src/TriangleSet.cpp | 69 ++++++++++++++++++++++++++++ libraries/shared/src/TriangleSet.h | 33 +++++++++++++ 5 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 libraries/shared/src/TriangleSet.cpp create mode 100644 libraries/shared/src/TriangleSet.h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index adfffe2614..9dd3563887 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -376,24 +376,26 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g } for (const auto& subMeshBox : _calculatedMeshBoxes) { + bool intersectedSubMesh = false; + float subMeshDistance = std::numeric_limits::max(); if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace, subMeshSurfaceNormal)) { if (distanceToSubMesh < bestDistance) { if (pickAgainstTriangles) { - // check our triangles here.... - const QVector& 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); - } - } + + float subMeshDistance; + glm::vec3 subMeshNormal; + const auto& meshTriangleSet = _meshTriangleSets[subMeshIndex]; + bool intersectedMesh = meshTriangleSet.findRayIntersection(origin, direction, subMeshDistance, subMeshNormal); + + if (intersectedMesh && subMeshDistance < bestDistance) { + bestDistance = subMeshDistance; + intersectedSomething = true; + face = subMeshFace; + surfaceNormal = subMeshNormal; + extraInfo = geometry.getModelNameOfMesh(subMeshIndex); } + } else { // this is the non-triangle picking case... bestDistance = distanceToSubMesh; @@ -401,9 +403,13 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g face = subMeshFace; surfaceNormal = subMeshSurfaceNormal; extraInfo = geometry.getModelNameOfMesh(subMeshIndex); + + intersectedSubMesh = true; } } } + + subMeshIndex++; } _mutex.unlock(); @@ -451,18 +457,8 @@ bool Model::convexHullContains(glm::vec3 point) { 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& 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; - } - } - if (insideMesh) { + if (_meshTriangleSets[subMeshIndex].convexHullContains(point)) { // It's inside this mesh, return true. _mutex.unlock(); return true; @@ -486,12 +482,20 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid; if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) { + + if (pickAgainstTriangles) { + qDebug() << "RECALCULATING triangles!"; + } else { + qDebug() << "RECALCULATING boxes!"; + } + const FBXGeometry& geometry = getFBXGeometry(); int numberOfMeshes = geometry.meshes.size(); _calculatedMeshBoxes.resize(numberOfMeshes); - _calculatedMeshTriangles.clear(); - _calculatedMeshTriangles.resize(numberOfMeshes); _calculatedMeshPartBoxes.clear(); + _meshTriangleSets.clear(); + _meshTriangleSets.resize(numberOfMeshes); + for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation); @@ -499,6 +503,8 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { _calculatedMeshBoxes[i] = AABox(scaledMeshExtents); if (pickAgainstTriangles) { + auto& thisMeshTriangleSet = _meshTriangleSets[i]; + QVector thisMeshTriangles; for (int j = 0; j < mesh.parts.size(); j++) { const FBXMeshPart& part = mesh.parts.at(j); @@ -550,6 +556,9 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { thisMeshTriangles.push_back(tri1); thisMeshTriangles.push_back(tri2); + thisMeshTriangleSet.insertTriangle(tri1); + thisMeshTriangleSet.insertTriangle(tri2); + } } @@ -582,11 +591,13 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { Triangle tri = { v0, v1, v2 }; thisMeshTriangles.push_back(tri); + + thisMeshTriangleSet.insertTriangle(tri); + } } _calculatedMeshPartBoxes[QPair(i, j)] = thisPartBounds; } - _calculatedMeshTriangles[i] = thisMeshTriangles; _calculatedMeshPartBoxesValid = true; } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 7c373274e4..bc3cf0e521 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "GeometryCache.h" #include "TextureCache.h" @@ -362,8 +363,8 @@ protected: bool _calculatedMeshPartBoxesValid; QVector _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes bool _calculatedMeshBoxesValid; + QVector< TriangleSet > _meshTriangleSets; // world coordinate triangles for all sub meshes - QVector< QVector > _calculatedMeshTriangles; // world coordinate triangles for all sub meshes bool _calculatedMeshTrianglesValid; QMutex _mutex; diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 2f0b09d67a..ccc7b6e302 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -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; diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp new file mode 100644 index 0000000000..3264c259d3 --- /dev/null +++ b/libraries/shared/src/TriangleSet.cpp @@ -0,0 +1,69 @@ +// +// 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::insertTriangle(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, glm::vec3& surfaceNormal) const { + + float bestDistance = std::numeric_limits::max(); + float thisTriangleDistance; + bool intersectedSomething = false; + + for (const auto& triangle : _triangles) { + if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + intersectedSomething = true; + surfaceNormal = triangle.getNormal(); + distance = bestDistance; + //face = subMeshFace; + //extraInfo = geometry.getModelNameOfMesh(subMeshIndex); + } + } + } + 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; +} + diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h new file mode 100644 index 0000000000..3aef3966b6 --- /dev/null +++ b/libraries/shared/src/TriangleSet.h @@ -0,0 +1,33 @@ +// +// 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 + +#include "AABox.h" +#include "GeometryUtil.h" + +class TriangleSet { +public: + void insertTriangle(const Triangle& t); + void 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 findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, glm::vec3& surfaceNormal) const; + + bool convexHullContains(const glm::vec3& point) const; // this point is "inside" all triangles + const AABox& getBounds() const; + +private: + QVector _triangles; + AABox _bounds; +}; From 32add6635d8588106eddf2d1a235b6e118d2450d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 4 Mar 2017 23:33:17 -0800 Subject: [PATCH 2/7] checkpoint on reducing raypicking recalcs --- libraries/fbx/src/FBXReader.h | 2 +- libraries/render-utils/src/Model.cpp | 98 ++++++++++++++++------------ libraries/render-utils/src/Model.h | 6 +- 3 files changed, 62 insertions(+), 44 deletions(-) diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 5910b8d312..6e51c413dc 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -300,7 +300,7 @@ public: QHash 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; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9dd3563887..d94bb67590 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -375,6 +375,14 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g recalculateMeshBoxes(pickAgainstTriangles); } + glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); + glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); + glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); + + 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& subMeshBox : _calculatedMeshBoxes) { bool intersectedSubMesh = false; float subMeshDistance = std::numeric_limits::max(); @@ -383,19 +391,24 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (distanceToSubMesh < bestDistance) { if (pickAgainstTriangles) { - float subMeshDistance; + float subMeshDistance = 0.0f; glm::vec3 subMeshNormal; - const auto& meshTriangleSet = _meshTriangleSets[subMeshIndex]; - bool intersectedMesh = meshTriangleSet.findRayIntersection(origin, direction, subMeshDistance, subMeshNormal); + const auto& meshTriangleSet = _modelSpaceMeshTriangleSets[subMeshIndex]; + bool intersectedMesh = meshTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, subMeshDistance, subMeshNormal); - if (intersectedMesh && subMeshDistance < bestDistance) { - bestDistance = subMeshDistance; - intersectedSomething = true; - face = subMeshFace; - surfaceNormal = subMeshNormal; - extraInfo = geometry.getModelNameOfMesh(subMeshIndex); + 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; @@ -458,7 +471,13 @@ bool Model::convexHullContains(glm::vec3 point) { foreach(const AABox& subMeshBox, _calculatedMeshBoxes) { if (subMeshBox.contains(point)) { - if (_meshTriangleSets[subMeshIndex].convexHullContains(point)) { + glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); + glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); + glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); + + glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f)); + + if (_modelSpaceMeshTriangleSets[subMeshIndex].convexHullContains(meshFramePoint)) { // It's inside this mesh, return true. _mutex.unlock(); return true; @@ -493,8 +512,9 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { int numberOfMeshes = geometry.meshes.size(); _calculatedMeshBoxes.resize(numberOfMeshes); _calculatedMeshPartBoxes.clear(); - _meshTriangleSets.clear(); - _meshTriangleSets.resize(numberOfMeshes); + + _modelSpaceMeshTriangleSets.clear(); + _modelSpaceMeshTriangleSets.resize(numberOfMeshes); for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); @@ -503,9 +523,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { _calculatedMeshBoxes[i] = AABox(scaledMeshExtents); if (pickAgainstTriangles) { - auto& thisMeshTriangleSet = _meshTriangleSets[i]; - QVector thisMeshTriangles; for (int j = 0; j < mesh.parts.size(); j++) { const FBXMeshPart& part = mesh.parts.at(j); @@ -540,24 +558,21 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { thisPartBounds += mv2; thisPartBounds += mv3; - glm::vec3 v0 = calculateScaledOffsetPoint(mv0); - glm::vec3 v1 = calculateScaledOffsetPoint(mv1); - glm::vec3 v2 = calculateScaledOffsetPoint(mv2); - glm::vec3 v3 = calculateScaledOffsetPoint(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)); - // 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); - - thisMeshTriangleSet.insertTriangle(tri1); - thisMeshTriangleSet.insertTriangle(tri2); + Triangle tri1 = { v0, v1, v3 }; + Triangle tri2 = { v1, v2, v3 }; + _modelSpaceMeshTriangleSets[i].insertTriangle(tri1); + _modelSpaceMeshTriangleSets[i].insertTriangle(tri2); + } } } @@ -584,15 +599,18 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { thisPartBounds += mv1; thisPartBounds += mv2; - glm::vec3 v0 = calculateScaledOffsetPoint(mv0); - glm::vec3 v1 = calculateScaledOffsetPoint(mv1); - glm::vec3 v2 = calculateScaledOffsetPoint(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 }; - - thisMeshTriangles.push_back(tri); - - thisMeshTriangleSet.insertTriangle(tri); + Triangle tri = { v0, v1, v2 }; + _modelSpaceMeshTriangleSets[i].insertTriangle(tri); + } } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index bc3cf0e521..763112cebc 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -294,10 +294,10 @@ 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; - glm::vec3 _offset; + glm::vec3 _offset; // this is the translation for the minimum extent of the model (in original mesh coordinate space) to the model's registration point static float FAKE_DIMENSION_PLACEHOLDER; @@ -363,7 +363,7 @@ protected: bool _calculatedMeshPartBoxesValid; QVector _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes bool _calculatedMeshBoxesValid; - QVector< TriangleSet > _meshTriangleSets; // world coordinate triangles for all sub meshes + QVector _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes bool _calculatedMeshTrianglesValid; QMutex _mutex; From 87bcced409ea42d4e414ac308b0823afb1e3feed Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 5 Mar 2017 19:23:55 -0800 Subject: [PATCH 3/7] cleanup use of triangleSet for picking --- .../src/RenderableModelEntityItem.cpp | 6 + libraries/render-utils/src/Model.cpp | 333 ++++++------------ libraries/render-utils/src/Model.h | 32 +- libraries/shared/src/TriangleSet.cpp | 31 +- libraries/shared/src/TriangleSet.h | 4 +- 5 files changed, 134 insertions(+), 272 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e6902228c5..1a6daf3d43 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -418,6 +418,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // Enqueue updates for the next frame 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(); // FIXME: this seems like it could be optimized if we tracked our last known visible state in diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d94bb67590..172290eae5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -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,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 // and testing intersection there. if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { + QMutexLocker locker(&_mutex); + float bestDistance = std::numeric_limits::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(); } 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 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) { - bool intersectedSubMesh = false; - float subMeshDistance = std::numeric_limits::max(); + glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); + glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); + float worldDistance = glm::distance(origin, worldIntersectionPoint); - if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace, subMeshSurfaceNormal)) { - if (distanceToSubMesh < bestDistance) { - if (pickAgainstTriangles) { - - float subMeshDistance = 0.0f; - glm::vec3 subMeshNormal; - 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; - } + 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; @@ -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 // 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)) { + glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); + glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); + 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); - glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); - glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); - - glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f)); - - if (_modelSpaceMeshTriangleSets[subMeshIndex].convexHullContains(meshFramePoint)) { + 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(); - if (pickAgainstTriangles) { - qDebug() << "RECALCULATING triangles!"; - } else { - qDebug() << "RECALCULATING boxes!"; - } + _triangleSetsValid = true; + _modelSpaceMeshTriangleSets.clear(); + _modelSpaceMeshTriangleSets.resize(numberOfMeshes); - const FBXGeometry& geometry = getFBXGeometry(); - int numberOfMeshes = geometry.meshes.size(); - _calculatedMeshBoxes.resize(numberOfMeshes); - _calculatedMeshPartBoxes.clear(); + for (int i = 0; i < numberOfMeshes; i++) { + const FBXMesh& mesh = geometry.meshes.at(i); - _modelSpaceMeshTriangleSets.clear(); - _modelSpaceMeshTriangleSets.resize(numberOfMeshes); + 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); - Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents, _translation, _rotation); + const int INDICES_PER_TRIANGLE = 3; + const int INDICES_PER_QUAD = 4; - _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++) { - const FBXMeshPart& part = mesh.parts.at(j); - - bool atLeastOnePointInBounds = false; - AABox thisPartBounds; - - const int INDICES_PER_TRIANGLE = 3; - const int INDICES_PER_QUAD = 4; - - 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++]; - - 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)); - - // 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; - - // 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(i, j)] = thisPartBounds; + 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++]; + + // 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)); + + Triangle tri = { v0, v1, v2 }; + _modelSpaceMeshTriangleSets[i].insertTriangle(tri); } - _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 > dilated; - dilated.resize(mesh.parts.size()); - _dilatedTextures.append(dilated); - } - } - - if (!_addedToScene && isLoaded()) { - createRenderItemSet(); } } @@ -752,7 +635,15 @@ void Model::removeFromScene(std::shared_ptr 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 = 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) { _debugMeshBoxesID = DependencyManager::get()->allocateID(); } @@ -784,8 +675,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 @@ -843,37 +734,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); } @@ -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 // 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; + //_calculatedMeshBoxesValid = false; + //_calculatedMeshTrianglesValid = false; + + // FIXME -- if the model URL changes, then we need to recalculate the triangle sets??!!!! + onInvalidate(); // check for scale to fit diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 763112cebc..05626d1ebe 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -96,7 +96,6 @@ public: render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); - void renderSetup(RenderArgs* args); bool isRenderable() const; bool isVisible() const { return _isVisible; } @@ -251,6 +250,9 @@ public: uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } const QMap& getRenderItems() const { return _modelMeshRenderItems; } + void renderDebugMeshBoxes(gpu::Batch& batch); + + public slots: void loadURLFinished(bool success); @@ -267,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); @@ -332,14 +325,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(); @@ -358,17 +350,12 @@ protected: int _blendNumber; int _appliedBlendNumber; - QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes - - bool _calculatedMeshPartBoxesValid; - QVector _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes - bool _calculatedMeshBoxesValid; - QVector _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes - - bool _calculatedMeshTrianglesValid; QMutex _mutex; - void recalculateMeshBoxes(bool pickAgainstTriangles = false); + bool _triangleSetsValid { false }; + void calculateTriangleSets(); + QVector _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes + void createRenderItemSet(); virtual void createVisibleRenderItemSet(); @@ -377,7 +364,6 @@ protected: bool _isWireframe; // debug rendering support - void renderDebugMeshBoxes(gpu::Batch& batch); int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 3264c259d3..5a634acc77 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -28,24 +28,31 @@ void TriangleSet::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, glm::vec3& surfaceNormal) const { + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const { - float bestDistance = std::numeric_limits::max(); - float thisTriangleDistance; bool intersectedSomething = false; + float boxDistance = std::numeric_limits::max(); + float bestDistance = std::numeric_limits::max(); - for (const auto& triangle : _triangles) { - if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - surfaceNormal = triangle.getNormal(); - distance = bestDistance; - //face = subMeshFace; - //extraInfo = geometry.getModelNameOfMesh(subMeshIndex); + 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; } diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 3aef3966b6..8024057174 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -22,10 +22,10 @@ public: // 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 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 - const AABox& getBounds() const; + const AABox& getBounds() const { return _bounds; } private: QVector _triangles; From 2a663cbcb16352c043e38f6f6744e1615045df5d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 5 Mar 2017 19:28:15 -0800 Subject: [PATCH 4/7] cleanup --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 2 +- libraries/render-utils/src/Model.cpp | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 1a6daf3d43..935dd4e796 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -418,7 +418,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // Enqueue updates for the next frame if (_model) { -#if 1 //def WANT_EXTRA_RENDER_DEBUGGING +#ifdef WANT_EXTRA_RENDER_DEBUGGING // debugging... gpu::Batch& batch = *args->_batch; _model->renderDebugMeshBoxes(batch); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 172290eae5..6be23ad615 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1015,15 +1015,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; - - // FIXME -- if the model URL changes, then we need to recalculate the triangle sets??!!!! - onInvalidate(); // check for scale to fit From af97e9bdd9f1ec8bece9166253f04044b828f8c7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 6 Mar 2017 10:49:38 -0800 Subject: [PATCH 5/7] CR feedback --- libraries/render-utils/src/Model.cpp | 21 +++++++++++++-------- libraries/render-utils/src/Model.h | 6 +++++- libraries/shared/src/TriangleSet.cpp | 2 +- libraries/shared/src/TriangleSet.h | 13 +++++++++---- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6be23ad615..ae20823138 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -368,7 +368,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g } glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); + glm::mat4 meshToWorldMatrix = meshToModelMatrix * createMatFromQuatAndPos(_rotation, _translation); glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f)); @@ -437,7 +437,7 @@ bool Model::convexHullContains(glm::vec3 point) { // If we are inside the models box, then consider the submeshes... glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); + glm::mat4 meshToWorldMatrix = meshToModelMatrix * createMatFromQuatAndPos(_rotation, _translation); glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f)); @@ -475,9 +475,15 @@ void Model::calculateTriangleSets() { const int INDICES_PER_TRIANGLE = 3; const int INDICES_PER_QUAD = 4; + const int TRIANGLES_PER_QUAD = 2; + + // 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); 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++]; @@ -495,13 +501,12 @@ void Model::calculateTriangleSets() { Triangle tri1 = { v0, v1, v3 }; Triangle tri2 = { v1, v2, v3 }; - _modelSpaceMeshTriangleSets[i].insertTriangle(tri1); - _modelSpaceMeshTriangleSets[i].insertTriangle(tri2); + _modelSpaceMeshTriangleSets[i].insert(tri1); + _modelSpaceMeshTriangleSets[i].insert(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++]; @@ -516,7 +521,7 @@ void Model::calculateTriangleSets() { glm::vec3 v2 = glm::vec3(getFBXGeometry().offset * glm::vec4(mesh.vertices[i2], 1.0f)); Triangle tri = { v0, v1, v2 }; - _modelSpaceMeshTriangleSets[i].insertTriangle(tri); + _modelSpaceMeshTriangleSets[i].insert(tri); } } } @@ -637,7 +642,7 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) { _mutex.lock(); glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = meshToModelMatrix * glm::translate(_translation) * glm::mat4_cast(_rotation); + glm::mat4 meshToWorldMatrix = meshToModelMatrix * createMatFromQuatAndPos(_rotation, _translation); Transform meshToWorld(meshToWorldMatrix); batch.setModelTransform(meshToWorld); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 05626d1ebe..41821736f7 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -290,7 +290,11 @@ protected: glm::vec3 _translation; // this is the translation in world coordinates to the model's registration point glm::quat _rotation; glm::vec3 _scale; - glm::vec3 _offset; // this is the translation for the minimum extent of the model (in original mesh coordinate space) to the model's registration point + + // 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; diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 5a634acc77..cdb3fd6b2c 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -12,7 +12,7 @@ #include "GLMHelpers.h" #include "TriangleSet.h" -void TriangleSet::insertTriangle(const Triangle& t) { +void TriangleSet::insert(const Triangle& t) { _triangles.push_back(t); _bounds += t.v0; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 8024057174..cc9dd1cc6d 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -16,15 +16,20 @@ class TriangleSet { public: - void insertTriangle(const Triangle& t); + void reserve(size_t size) { _triangles.reserve((int)size); } // reserve space in the datastructure for size number of triangles + + void insert(const Triangle& t); void 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. + // 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; - bool convexHullContains(const glm::vec3& point) const; // this point is "inside" all triangles + // 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: From faba16033103ae971b91a01f8ddd739dfd7b9995 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 6 Mar 2017 11:10:04 -0800 Subject: [PATCH 6/7] more CR feedback --- libraries/shared/src/TriangleSet.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index cc9dd1cc6d..87336c77af 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -9,14 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include "AABox.h" #include "GeometryUtil.h" class TriangleSet { public: - void reserve(size_t size) { _triangles.reserve((int)size); } // reserve space in the datastructure for size number of triangles + void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles void insert(const Triangle& t); void clear(); @@ -33,6 +33,6 @@ public: const AABox& getBounds() const { return _bounds; } private: - QVector _triangles; + std::vector _triangles; AABox _bounds; }; From 87934ee82d1c1a247311ec3886d3c278aa40d8a3 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 6 Mar 2017 21:50:32 -0800 Subject: [PATCH 7/7] fix bugs in some meshes --- libraries/render-utils/src/Model.cpp | 24 ++++++++++++++---------- libraries/shared/src/TriangleSet.h | 5 ++++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2af42c046e..48c1d29b68 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -368,7 +368,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g } glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = meshToModelMatrix * createMatFromQuatAndPos(_rotation, _translation); + glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix; glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f)); @@ -437,7 +437,7 @@ bool Model::convexHullContains(glm::vec3 point) { // If we are inside the models box, then consider the submeshes... glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = meshToModelMatrix * createMatFromQuatAndPos(_rotation, _translation); + glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix; glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f)); @@ -483,6 +483,8 @@ void Model::calculateTriangleSets() { int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris; _modelSpaceMeshTriangleSets[i].reserve(totalTriangles); + auto meshTransform = getFBXGeometry().offset * mesh.modelTransform; + if (part.quadIndices.size() > 0) { int vIndex = 0; for (int q = 0; q < numberOfQuads; q++) { @@ -494,10 +496,10 @@ void Model::calculateTriangleSets() { // 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)); + 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)); Triangle tri1 = { v0, v1, v3 }; Triangle tri2 = { v1, v2, v3 }; @@ -516,9 +518,9 @@ void Model::calculateTriangleSets() { // 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 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); @@ -642,10 +644,12 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) { _mutex.lock(); glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = meshToModelMatrix * createMatFromQuatAndPos(_rotation, _translation); + glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix; Transform meshToWorld(meshToWorldMatrix); batch.setModelTransform(meshToWorld); + DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true, true); + for(const auto& triangleSet : _modelSpaceMeshTriangleSets) { auto box = triangleSet.getBounds(); diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 87336c77af..b54f1a642a 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -16,7 +16,10 @@ class TriangleSet { public: - void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles + 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();