From dc5c5c20df84b87c64e74a8bb18684a678df7418 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 11 Jun 2018 15:44:19 -0400 Subject: [PATCH] raypick partIndex commit include shapeID for accessing proper material index add raypick shapeID test script --- libraries/entities/src/EntityItem.h | 3 +- libraries/entities/src/EntityTreeElement.cpp | 4 +- libraries/render-utils/src/Model.cpp | 170 ++++++++++--------- libraries/render-utils/src/Model.h | 3 +- libraries/shared/src/TriangleSet.h | 2 + scripts/developer/tests/raypickTester.js | 73 ++++++++ 6 files changed, 174 insertions(+), 81 deletions(-) create mode 100644 scripts/developer/tests/raypickTester.js diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 0acf8dbbc1..84391382fc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -185,6 +185,7 @@ public: /// Dimensions in meters (0.0 - TREE_SCALE) glm::vec3 getScaledDimensions() const; virtual void setScaledDimensions(const glm::vec3& value); + virtual glm::vec3 getRaycastDimensions() const { return getScaledDimensions(); } inline const glm::vec3 getUnscaledDimensions() const { return _unscaledDimensions; } virtual void setUnscaledDimensions(const glm::vec3& value); @@ -239,7 +240,7 @@ public: // position, size, and bounds related helpers virtual AACube getMaximumAACube(bool& success) const override; AACube getMinimumAACube(bool& success) const; - AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters) + virtual AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters) using SpatiallyNestable::getQueryAACube; virtual AACube getQueryAACube(bool& success) const override; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index cbcddfc57b..bc5bb1e81d 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -214,7 +214,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori glm::mat4 entityToWorldMatrix = translation * rotation; glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - glm::vec3 dimensions = entity->getScaledDimensions(); + glm::vec3 dimensions = entity->getRaycastDimensions(); glm::vec3 registrationPoint = entity->getRegistrationPoint(); glm::vec3 corner = -(dimensions * registrationPoint); @@ -312,7 +312,7 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc glm::vec3 penetration; if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) { - glm::vec3 dimensions = entity->getScaledDimensions(); + glm::vec3 dimensions = entity->getRaycastDimensions(); // FIXME - consider allowing the entity to determine penetration so that // entities could presumably dull actuall hull testing if they wanted to diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 178c00c4c7..dc65863c6e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -401,26 +401,34 @@ 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 (auto& triangleSet : _modelSpaceMeshTriangleSets) { - float triangleSetDistance = 0.0f; - BoxFace triangleSetFace; - Triangle triangleSetTriangle; - if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { + int shapeID = 0; + for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { + int partIndex = 0; + for (auto &partTriangleSet : meshTriangleSets) { + float triangleSetDistance = 0.0f; + BoxFace triangleSetFace; + Triangle triangleSetTriangle; + if (partTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { - glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); - glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); - float worldDistance = glm::distance(origin, worldIntersectionPoint); + 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; - bestModelTriangle = triangleSetTriangle; - bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; - extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); - extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); - bestSubMeshIndex = subMeshIndex; + if (worldDistance < bestDistance) { + bestDistance = worldDistance; + intersectedSomething = true; + face = triangleSetFace; + bestModelTriangle = triangleSetTriangle; + bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; + extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); + extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); + extraInfo["partIndex"] = partIndex; + extraInfo["shapeID"] = shapeID; + bestSubMeshIndex = subMeshIndex; + } } + partIndex++; + shapeID++; } subMeshIndex++; } @@ -485,12 +493,14 @@ bool Model::convexHullContains(glm::vec3 point) { glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f)); - 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. - return true; + for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { + for (auto &partTriangleSet : meshTriangleSets) { + const AABox& box = partTriangleSet.getBounds(); + if (box.contains(meshFramePoint)) { + if (partTriangleSet.convexHullContains(meshFramePoint)) { + // It's inside this mesh, return true. + return true; + } } } } @@ -653,9 +663,15 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); - for (int j = 0; j < mesh.parts.size(); j++) { + const int numberOfParts = mesh.parts.size(); + auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i]; + meshTriangleSets.resize(numberOfParts); + + for (int j = 0; j < numberOfParts; j++) { const FBXMeshPart& part = mesh.parts.at(j); + auto& partTriangleSet = meshTriangleSets[j]; + const int INDICES_PER_TRIANGLE = 3; const int INDICES_PER_QUAD = 4; const int TRIANGLES_PER_QUAD = 2; @@ -664,7 +680,7 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { 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); + partTriangleSet.reserve(totalTriangles); auto meshTransform = geometry.offset * mesh.modelTransform; @@ -686,8 +702,8 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { Triangle tri1 = { v0, v1, v3 }; Triangle tri2 = { v1, v2, v3 }; - _modelSpaceMeshTriangleSets[i].insert(tri1); - _modelSpaceMeshTriangleSets[i].insert(tri2); + partTriangleSet.insert(tri1); + partTriangleSet.insert(tri2); } } @@ -706,7 +722,7 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f)); Triangle tri = { v0, v1, v2 }; - _modelSpaceMeshTriangleSets[i].insert(tri); + partTriangleSet.insert(tri); } } } @@ -876,56 +892,58 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) { DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true, true); - for (const auto& triangleSet : _modelSpaceMeshTriangleSets) { - auto box = triangleSet.getBounds(); + for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { + for (auto &partTriangleSet : meshTriangleSets) { + auto box = partTriangleSet.getBounds(); - if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) { - _debugMeshBoxesID = DependencyManager::get()->allocateID(); + if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) { + _debugMeshBoxesID = DependencyManager::get()->allocateID(); + } + QVector points; + + glm::vec3 brn = box.getCorner(); + glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0); + glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z); + glm::vec3 blf = brn + glm::vec3(box.getDimensions().x, 0, box.getDimensions().z); + + glm::vec3 trn = brn + glm::vec3(0, box.getDimensions().y, 0); + glm::vec3 tln = bln + glm::vec3(0, box.getDimensions().y, 0); + glm::vec3 trf = brf + glm::vec3(0, box.getDimensions().y, 0); + glm::vec3 tlf = blf + glm::vec3(0, box.getDimensions().y, 0); + + points << brn << bln; + points << brf << blf; + points << brn << brf; + points << bln << blf; + + points << trn << tln; + points << trf << tlf; + points << trn << trf; + points << tln << tlf; + + points << brn << trn; + points << brf << trf; + points << bln << tln; + points << blf << tlf; + + glm::vec4 color[] = { + { 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 + { 0.0f, 1.0f, 1.0f, 1.0f }, // cyan + { 1.0f, 1.0f, 1.0f, 1.0f }, // white + { 0.0f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.5f, 0.5f, 1.0f } }; + + DependencyManager::get()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]); + DependencyManager::get()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID); + colorNdx++; } - QVector points; - - glm::vec3 brn = box.getCorner(); - glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0); - glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z); - glm::vec3 blf = brn + glm::vec3(box.getDimensions().x, 0, box.getDimensions().z); - - glm::vec3 trn = brn + glm::vec3(0, box.getDimensions().y, 0); - glm::vec3 tln = bln + glm::vec3(0, box.getDimensions().y, 0); - glm::vec3 trf = brf + glm::vec3(0, box.getDimensions().y, 0); - glm::vec3 tlf = blf + glm::vec3(0, box.getDimensions().y, 0); - - points << brn << bln; - points << brf << blf; - points << brn << brf; - points << bln << blf; - - points << trn << tln; - points << trf << tlf; - points << trn << trf; - points << tln << tlf; - - points << brn << trn; - points << brf << trf; - points << bln << tln; - points << blf << tlf; - - glm::vec4 color[] = { - { 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 - { 0.0f, 1.0f, 1.0f, 1.0f }, // cyan - { 1.0f, 1.0f, 1.0f, 1.0f }, // white - { 0.0f, 0.5f, 0.0f, 1.0f }, - { 0.0f, 0.0f, 0.5f, 1.0f }, - { 0.5f, 0.0f, 0.5f, 1.0f }, - { 0.5f, 0.5f, 0.0f, 1.0f }, - { 0.0f, 0.5f, 0.5f, 1.0f } }; - - DependencyManager::get()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]); - DependencyManager::get()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID); - colorNdx++; } _mutex.unlock(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index bc82a0d335..0bddae6a38 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -423,8 +423,7 @@ protected: bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; void calculateTriangleSets(const FBXGeometry& geometry); - QVector _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes - + std::vector> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes virtual void createRenderItemSet(); diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 9f688f9def..2853d0f68e 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#pragma once + #include #include "AABox.h" diff --git a/scripts/developer/tests/raypickTester.js b/scripts/developer/tests/raypickTester.js new file mode 100644 index 0000000000..a6704fc55c --- /dev/null +++ b/scripts/developer/tests/raypickTester.js @@ -0,0 +1,73 @@ +// raypickTester.js +// +// display intersection details (including material) when hovering over entities/avatars/overlays +// + +/* eslint-disable comma-dangle, no-empty, no-magic-numbers */ + +var PICK_FILTERS = Picks.PICK_ENTITIES | Picks.PICK_OVERLAYS | Picks.PICK_AVATARS | Picks.PICK_INCLUDE_NONCOLLIDABLE; +var HAND_JOINT = '_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND'.replace('RIGHT', MyAvatar.getDominantHand().toUpperCase()); +var JOINT_NAME = HMD.active ? HAND_JOINT : 'Mouse'; +var UPDATE_MS = 1000/30; + +// create tect3d overlay to display hover results +var overlayID = Overlays.addOverlay('text3d', { + text: 'hover', + visible: false, + backgroundAlpha: 0, + isFacingAvatar: true, + lineHeight: 0.05, + dimensions: Vec3.HALF, +}); +Script.scriptEnding.connect(function() { + Overlays.deleteOverlay(overlayID); +}); + +// create raycast picker +var pickID = Picks.createPick(PickType.Ray, { + joint: JOINT_NAME, + filter: PICK_FILTERS, + enabled: true, +}); +var blacklist = [ overlayID ]; // exclude hover text from ray pick results +Picks.setIgnoreItems(pickID, blacklist); +Script.scriptEnding.connect(function() { + RayPick.removeRayPick(pickID); +}); + +// query object materials (using the Graphics.* API) +function getSubmeshMaterial(objectID, shapeID) { + try { + var materialLayers = Graphics.getModel(objectID).materialLayers; + var shapeMaterialLayers = materialLayers[shapeID]; + return shapeMaterialLayers[0].material; + } catch (e) { + return { name: '' }; + } +} + +// refresh hover overlay text based on intersection results +function updateOverlay(overlayID, result) { + var material = this.getSubmeshMaterial(result.objectID, result.extraInfo.shapeID); + var position = Vec3.mix(result.searchRay.origin, result.intersection, 0.5); + var extraInfo = result.extraInfo; + var text = [ + 'mesh: ' + extraInfo.subMeshName, + 'materialName: ' + material.name, + 'type: ' + Entities.getNestableType(result.objectID), + 'distance: ' + result.distance.toFixed(2)+'m', + ['submesh: ' + extraInfo.subMeshIndex, 'part: '+extraInfo.partIndex, 'shape: '+extraInfo.shapeID].join(' | '), + ].filter(Boolean).join('\n'); + + Overlays.editOverlay(overlayID, { + text: text, + position: position, + visible: result.intersects, + }); +} + +// monitor for enw results at 30fps +Script.setInterval(function() { + var result = RayPick.getPrevRayPickResult(pickID); + updateOverlay(overlayID, result); +}, UPDATE_MS);