ensure brute force ray intersection works (#1170)

* ensure brute force ray intersection works in the same space as kd-tree intersection

* add additional tests for ray casts against meshes using brute force approach

* update api and add some additional test cases

* comment tidy-up and other small updates/fixes for ray intersection code

* fix issue with values at the end of a ray
This commit is contained in:
Tom Hulton-Harrop
2021-06-08 19:44:33 +01:00
committed by GitHub
parent 80f62d0523
commit dd95d2b02e
7 changed files with 279 additions and 143 deletions
@@ -15,6 +15,7 @@
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Math/IntersectSegment.h>
#include <AzCore/std/limits.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/SerializeContext.h>
@@ -75,7 +76,8 @@ namespace AZ
m_status = Data::AssetData::AssetStatus::Ready;
}
bool ModelAsset::LocalRayIntersectionAgainstModel(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance, AZ::Vector3& normal) const
bool ModelAsset::LocalRayIntersectionAgainstModel(
const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender);
@@ -85,7 +87,7 @@ namespace AZ
m_modelTriangleCount = CalculateTriangleCount();
}
// check the total vertex count for this model and skip kdtree if the model is simple enough
// check the total vertex count for this model and skip kd-tree if the model is simple enough
if (*m_modelTriangleCount > s_minimumModelTriangleCountToOptimize)
{
if (!m_kdTree)
@@ -97,11 +99,11 @@ namespace AZ
}
else
{
return m_kdTree->RayIntersection(rayStart, dir, distance, normal);
return m_kdTree->RayIntersection(rayStart, rayDir, distanceNormalized, normal);
}
}
return BruteForceRayIntersect(rayStart, dir, distance, normal);
return BruteForceRayIntersect(rayStart, rayDir, distanceNormalized, normal);
}
void ModelAsset::BuildKdTree() const
@@ -136,7 +138,8 @@ namespace AZ
}
}
bool ModelAsset::BruteForceRayIntersect(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance, AZ::Vector3& normal) const
bool ModelAsset::BruteForceRayIntersect(
const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const
{
// brute force - check every triangle
if (GetLodAssets().empty() == false)
@@ -144,27 +147,27 @@ namespace AZ
// intersect against the highest level of detail
if (ModelLodAsset* loadAssetPtr = GetLodAssets()[0].Get())
{
float shortestDistance = std::numeric_limits<float>::max();
bool anyHit = false;
AZ::Vector3 intersectionNormal;
float shortestDistanceNormalized = AZStd::numeric_limits<float>::max();
for (const ModelLodAsset::Mesh& mesh : loadAssetPtr->GetMeshes())
{
if (LocalRayIntersectionAgainstMesh(mesh, rayStart, dir, distance, intersectionNormal))
float currentDistanceNormalized;
if (LocalRayIntersectionAgainstMesh(mesh, rayStart, rayDir, currentDistanceNormalized, intersectionNormal))
{
anyHit = true;
if (distance < shortestDistance)
if (currentDistanceNormalized < shortestDistanceNormalized)
{
normal = intersectionNormal;
shortestDistance = distance;
shortestDistanceNormalized = currentDistanceNormalized;
}
}
}
if (anyHit)
{
distance = shortestDistance;
distanceNormalized = shortestDistanceNormalized;
}
return anyHit;
@@ -174,7 +177,12 @@ namespace AZ
return false;
}
bool ModelAsset::LocalRayIntersectionAgainstMesh(const ModelLodAsset::Mesh& mesh, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance, AZ::Vector3& normal) const
bool ModelAsset::LocalRayIntersectionAgainstMesh(
const ModelLodAsset::Mesh& mesh,
const AZ::Vector3& rayStart,
const AZ::Vector3& rayDir,
float& distanceNormalized,
AZ::Vector3& normal) const
{
const BufferAssetView& indexBufferView = mesh.GetIndexBufferAssetView();
const AZStd::array_view<ModelLodAsset::Mesh::StreamBufferInfo>& streamBufferList = mesh.GetStreamBufferInfoList();
@@ -217,14 +225,13 @@ namespace AZ
AZStd::array_view<uint8_t> indexRawBuffer = indexAssetViewPtr->GetBuffer();
RHI::BufferViewDescriptor indexRawDesc = indexAssetViewPtr->GetBufferViewDescriptor();
float closestNormalizedDistance = 1.f;
bool anyHit = false;
const AZ::Vector3 rayEnd = rayStart + dir * distance;
const AZ::Vector3 rayEnd = rayStart + rayDir;
AZ::Vector3 a, b, c;
AZ::Vector3 intersectionNormal;
float normalizedDistance = 1.f;
float shortestDistanceNormalized = AZStd::numeric_limits<float>::max();
const AZ::u32* indexPtr = reinterpret_cast<const AZ::u32*>(indexRawBuffer.data());
for (uint32_t indexIter = 0; indexIter <= indexRawDesc.m_elementCount - 3; indexIter += 3, indexPtr += 3)
{
@@ -247,20 +254,22 @@ namespace AZ
p = reinterpret_cast<const float*>(&positionRawBuffer[index2 * positionElementSize]);
c.Set(const_cast<float*>(p));
if (AZ::Intersect::IntersectSegmentTriangleCCW(rayStart, rayEnd, a, b, c, intersectionNormal, normalizedDistance))
float currentDistanceNormalized;
if (AZ::Intersect::IntersectSegmentTriangleCCW(rayStart, rayEnd, a, b, c, intersectionNormal, currentDistanceNormalized))
{
if (normalizedDistance < closestNormalizedDistance)
anyHit = true;
if (currentDistanceNormalized < shortestDistanceNormalized)
{
normal = intersectionNormal;
closestNormalizedDistance = normalizedDistance;
shortestDistanceNormalized = currentDistanceNormalized;
}
anyHit = true;
}
}
if (anyHit)
{
distance = closestNormalizedDistance * distance;
distanceNormalized = shortestDistanceNormalized;
}
return anyHit;