Merge pull request #13409 from humbletim/add-raypick-shapeID

Include shapeID in RayPick results
This commit is contained in:
Sam Gondelman 2018-06-26 10:25:39 -07:00 committed by GitHub
commit fbe54437b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 81 deletions

View file

@ -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;

View file

@ -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

View file

@ -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<GeometryCache>()->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<GeometryCache>()->allocateID();
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
}
QVector<glm::vec3> 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<GeometryCache>()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID);
colorNdx++;
}
QVector<glm::vec3> 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<GeometryCache>()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID);
colorNdx++;
}
_mutex.unlock();
}

View file

@ -423,8 +423,7 @@ protected:
bool _overrideModelTransform { false };
bool _triangleSetsValid { false };
void calculateTriangleSets(const FBXGeometry& geometry);
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
std::vector<std::vector<TriangleSet>> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
virtual void createRenderItemSet();

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <vector>
#include "AABox.h"

View file

@ -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() {
Picks.removePick(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: '<unknown>' };
}
}
// 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 = Picks.getPrevPickResult(pickID);
updateOverlay(overlayID, result);
}, UPDATE_MS);