mirror of
https://github.com/lubosz/overte.git
synced 2025-04-10 01:04:07 +02:00
Merge pull request #13409 from humbletim/add-raypick-shapeID
Include shapeID in RayPick results
This commit is contained in:
commit
fbe54437b8
6 changed files with 174 additions and 81 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
73
scripts/developer/tests/raypickTester.js
Normal file
73
scripts/developer/tests/raypickTester.js
Normal 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);
|
Loading…
Reference in a new issue