Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use selection buffer in ray question (ogre2) - part 2 #383

Merged
merged 13 commits into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ogre2/include/ignition/rendering/ogre2/Ogre2RayQuery.hh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ namespace ignition
// Documentation inherited
public: virtual RayQueryResult ClosestPoint();

/// \brief Get closest point by selection buffer.
/// This is executed on the GPU.
private: RayQueryResult ClosestPointBySelectionBuffer();

/// \brief Get closest point by ray triangle intersection test.
/// This is executed on the CPU.
private: RayQueryResult ClosestPointByIntersection();

/// \brief Private data pointer
private: std::unique_ptr<Ogre2RayQueryPrivate> dataPtr;

Expand Down
10 changes: 10 additions & 0 deletions ogre2/include/ignition/rendering/ogre2/Ogre2SelectionBuffer.hh
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ namespace ignition
/// \return Returns the Ogre item at the coordinate.
public: Ogre::Item *OnSelectionClick(const int _x, const int _y);

/// \brief Perform selection operation and get ogre item and
/// point of intersection.
/// \param[in] _x X coordinate in pixels.
/// \param[in] _y Y coordinate in pixels.
/// \param[out] Ogre item at the coordinate.
/// \param[out] 3D point of intersection with the ogre item's mesh.
/// \return True if an ogre item is found, false otherwise
public: bool ExecuteQuery(const int _x, const int _y, Ogre::Item *&_item,
math::Vector3d &_point);

/// \brief Debug show overlay
/// \param[in] _show True to show the selection buffer in an overlay.
// public: void ShowOverlay(const bool _show);
Expand Down
212 changes: 145 additions & 67 deletions ogre2/src/Ogre2RayQuery.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ class ignition::rendering::Ogre2RayQueryPrivate
//// \brief Pointer to camera
public: Ogre2CameraPtr camera{nullptr};

/// Image pos to cast the ray from
/// \brief Image pos to cast the ray from
public: math::Vector2i imgPos = math::Vector2i::Zero;

/// \brief thread that ray query is created in
public: std::thread::id threadId;
};

using namespace ignition;
Expand All @@ -47,6 +50,7 @@ using namespace rendering;
Ogre2RayQuery::Ogre2RayQuery()
: dataPtr(new Ogre2RayQueryPrivate)
{
this->dataPtr->threadId = std::this_thread::get_id();
}

//////////////////////////////////////////////////
Expand All @@ -69,97 +73,171 @@ void Ogre2RayQuery::SetFromCamera(const CameraPtr &_camera,

this->dataPtr->camera = camera;

this->dataPtr->imgPos.X() =
screenPos.X() * this->dataPtr->camera->ImageWidth();
this->dataPtr->imgPos.Y() =
screenPos.Y() * this->dataPtr->camera->ImageHeight();
this->dataPtr->imgPos.X() = static_cast<int>(
screenPos.X() * this->dataPtr->camera->ImageWidth());
this->dataPtr->imgPos.Y() = static_cast<int>(
screenPos.Y() * this->dataPtr->camera->ImageHeight());
}

//////////////////////////////////////////////////
RayQueryResult Ogre2RayQuery::ClosestPoint()
{
RayQueryResult result;
result.distance = -1;

// hack to create selection buffer if it does not exist yet
if (!this->dataPtr->camera->SelectionBuffer())
this->dataPtr->camera->VisualAt(this->dataPtr->imgPos);
#ifdef __APPLE__
return this->ClosestPointByIntersection();
#else
if (!this->dataPtr->camera ||
!this->dataPtr->camera->Parent() ||
std::this_thread::get_id() != this->dataPtr->threadId)
{
// use legacy method for backward compatibility if no camera is set or
// camera is not attached in the scene tree or
// this function is called from non-rendering thread
return this->ClosestPointByIntersection();
}
else
{
// the VisualAt function is a hack to force creation of the selection
// buffer object
// todo(anyone) Make Camera::SetSelectionBuffer function public?
if (!this->dataPtr->camera->SelectionBuffer())
this->dataPtr->camera->VisualAt(math::Vector2i(0, 0));

Ogre::Item *ogreItem =
this->dataPtr->camera->SelectionBuffer()->OnSelectionClick(
this->dataPtr->imgPos.X(), this->dataPtr->imgPos.Y());
return this->ClosestPointBySelectionBuffer();
}
#endif
}

if (!ogreItem)
return result;
//////////////////////////////////////////////////
RayQueryResult Ogre2RayQuery::ClosestPointBySelectionBuffer()
{
RayQueryResult result;
Ogre::Item *ogreItem = nullptr;
math::Vector3d point;
bool success = this->dataPtr->camera->SelectionBuffer()->ExecuteQuery(
this->dataPtr->imgPos.X(), this->dataPtr->imgPos.Y(), ogreItem, point);
result.distance = -1;

Ogre::Any userAny = ogreItem->getUserObjectBindings().getUserAny();
if (success && ogreItem)
{
if (!ogreItem->getUserObjectBindings().getUserAny().isEmpty() &&
ogreItem->getUserObjectBindings().getUserAny().getType() ==
typeid(unsigned int))
{
auto userAny = ogreItem->getUserObjectBindings().getUserAny();
double pointLength = point.Length();
if (!std::isinf(pointLength))
{
result.distance = pointLength;
result.point = point;
result.objectId = Ogre::any_cast<unsigned int>(userAny);
}
}
}
return result;
}

if (userAny.isEmpty() || userAny.getType() != typeid(unsigned int))
//////////////////////////////////////////////////
RayQueryResult Ogre2RayQuery::ClosestPointByIntersection()
{
RayQueryResult result;
Ogre2ScenePtr ogreScene =
std::dynamic_pointer_cast<Ogre2Scene>(this->Scene());
if (!ogreScene)
return result;

double distance = -1.0;

Ogre::Ray mouseRay(Ogre2Conversions::Convert(this->origin),
Ogre2Conversions::Convert(this->direction));

// mesh factory creates name with ::CENTER or ::ORIGINAL depending on
// the params passed in the MeshDescriptor when loading the mesh
// so strip off the suffix
std::string meshName = ogreItem->getMesh()->getName();
size_t idx = meshName.find("::");
if (idx != std::string::npos)
meshName = meshName.substr(0, idx);

const common::Mesh *mesh =
common::MeshManager::Instance()->MeshByName(meshName);
if (!this->dataPtr->rayQuery)
{
this->dataPtr->rayQuery =
ogreScene->OgreSceneManager()->createRayQuery(mouseRay);
}
this->dataPtr->rayQuery->setSortByDistance(true);
this->dataPtr->rayQuery->setRay(mouseRay);

if (!mesh)
return result;
// Perform the scene query
Ogre::RaySceneQueryResult &ogreResult = this->dataPtr->rayQuery->execute();

Ogre::Matrix4 transform = ogreItem->_getParentNodeFullTransform();
double distance = -1.0;

// test for hitting individual triangles on the mesh
for (unsigned int j = 0; j < mesh->SubMeshCount(); ++j)
// Iterate over all the results.
for (auto iter = ogreResult.begin(); iter != ogreResult.end(); ++iter)
{
auto s = mesh->SubMeshByIndex(j);
auto submesh = s.lock();
if (!submesh || submesh->VertexCount() < 3u)
if (iter->distance <= 0.0)
continue;

if (!iter->movable || !iter->movable->getVisible())
continue;
unsigned int indexCount = submesh->IndexCount();
for (unsigned int k = 0; k < indexCount; k += 3)

auto userAny = iter->movable->getUserObjectBindings().getUserAny();
if (!userAny.isEmpty() && userAny.getType() == typeid(unsigned int) &&
iter->movable->getMovableType() == "Item")
{
if (indexCount <= k+2)
Ogre::Item *ogreItem = static_cast<Ogre::Item *>(iter->movable);

// mesh factory creates name with ::CENTER or ::ORIGINAL depending on
// the params passed in the MeshDescriptor when loading the mesh
// so strip off the suffix
std::string meshName = ogreItem->getMesh()->getName();
size_t idx = meshName.find("::");
if (idx != std::string::npos)
meshName = meshName.substr(0, idx);

const common::Mesh *mesh =
common::MeshManager::Instance()->MeshByName(meshName);

if (!mesh)
continue;

ignition::math::Vector3d vertexA =
submesh->Vertex(submesh->Index(k));
ignition::math::Vector3d vertexB =
submesh->Vertex(submesh->Index(k+1));
ignition::math::Vector3d vertexC =
submesh->Vertex(submesh->Index(k+2));

Ogre::Vector3 worldVertexA =
transform * Ogre2Conversions::Convert(vertexA);
Ogre::Vector3 worldVertexB =
transform * Ogre2Conversions::Convert(vertexB);
Ogre::Vector3 worldVertexC =
transform * Ogre2Conversions::Convert(vertexC);

// check for a hit against this triangle
std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(mouseRay,
worldVertexA, worldVertexB, worldVertexC,
true, false);

// if it was a hit check if its the closest
if (hit.first &&
(distance < 0.0f || hit.second < distance))
Ogre::Matrix4 transform = ogreItem->_getParentNodeFullTransform();

// test for hitting individual triangles on the mesh
for (unsigned int j = 0; j < mesh->SubMeshCount(); ++j)
{
// this is the closest so far, save it off
distance = hit.second;
result.distance = distance;
result.point =
Ogre2Conversions::Convert(mouseRay.getPoint(distance));
result.objectId = Ogre::any_cast<unsigned int>(userAny);
auto s = mesh->SubMeshByIndex(j);
auto submesh = s.lock();
if (!submesh || submesh->VertexCount() < 3u)
continue;
unsigned int indexCount = submesh->IndexCount();
for (unsigned int k = 0; k < indexCount; k += 3)
{
if (indexCount <= k+2)
continue;

ignition::math::Vector3d vertexA =
submesh->Vertex(submesh->Index(k));
ignition::math::Vector3d vertexB =
submesh->Vertex(submesh->Index(k+1));
ignition::math::Vector3d vertexC =
submesh->Vertex(submesh->Index(k+2));

Ogre::Vector3 worldVertexA =
transform * Ogre2Conversions::Convert(vertexA);
Ogre::Vector3 worldVertexB =
transform * Ogre2Conversions::Convert(vertexB);
Ogre::Vector3 worldVertexC =
transform * Ogre2Conversions::Convert(vertexC);

// check for a hit against this triangle
std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(mouseRay,
worldVertexA, worldVertexB, worldVertexC,
true, false);

// if it was a hit check if its the closest
if (hit.first &&
(distance < 0.0f || hit.second < distance))
{
// this is the closest so far, save it off
distance = hit.second;
result.distance = distance;
result.point =
Ogre2Conversions::Convert(mouseRay.getPoint(distance));
result.objectId = Ogre::any_cast<unsigned int>(userAny);
}
}
}
}
}
Expand Down
Loading