Add a ray cast API to the terrain system, implement it, and use it so entities can be placed on top of terrain. (#6895)

* Add a ray cast API to the terrain system, implement it, and use it so entities can be placed on top of terrain.

Still to do:
- Unit tests
- Profiling/benchmarks
- Optimization (if needed, may not be now after updating to use the iterative approach, but one option could be to use SIMD to ray cast against both triangles at once)

Signed-off-by: bosnichd <bosnichd@amazon.com>

* Fix typos.

Signed-off-by: bosnichd <bosnichd@amazon.com>

* Added a FindNearestIntersectionIterative to use in place of the first-pass FindnearestIntersectionRecursive attempt.

Signed-off-by: bosnichd <bosnichd@amazon.com>

* Remove some functions that are no longer being used.

Signed-off-by: bosnichd <bosnichd@amazon.com>

* Remove a unicode character from a comment to fix the validation build.

Signed-off-by: bosnichd <bosnichd@amazon.com>

* Remove another unicode character from a comment to fix the validation build.

Signed-off-by: bosnichd <bosnichd@amazon.com>

* Update to bail out as soon as t > 1.0f

Signed-off-by: bosnichd <bosnichd@amazon.com>
This commit is contained in:
bosnichd
2022-01-24 13:47:44 -07:00
committed by GitHub
parent f8ac3071a3
commit f34d3a822e
9 changed files with 494 additions and 5 deletions
@@ -24,7 +24,7 @@ namespace AzFramework
//! IntersectorBus::EventResult(result, editorContextId, &IntersectorInterface::RayIntersect, ray);
//!
//! Raycast against all entities
//! RayResultAggregator rayResult;
//! AZ::EBusReduceResult<RayResult, RayResultClosestAggregator> rayResult;
//! IntersectorBus::BroadCastResult(rayResult, &IntersectorInterface::RayIntersect, ray);
//
class IntersectorInterface
@@ -12,6 +12,8 @@
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/containers/span.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Render/GeometryIntersectionStructures.h>
#include <AzFramework/SurfaceData/SurfaceData.h>
namespace AzFramework
@@ -185,6 +187,11 @@ namespace AzFramework
SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0;
//! Get the terrain raycast entity context id.
virtual EntityContextId GetTerrainRaycastEntityContextId() const = 0;
//! Given a ray, return the closest intersection with terrain.
virtual RenderGeometry::RayResult GetClosestIntersection(const RenderGeometry::RayRequest& ray) const = 0;
private:
// Private variations of the GetSurfacePoint API exposed to BehaviorContext that returns a value instead of
@@ -102,5 +102,9 @@ namespace UnitTest
ProcessSurfaceWeightsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
MOCK_CONST_METHOD4(
ProcessSurfacePointsFromRegion, void(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler));
MOCK_CONST_METHOD0(
GetTerrainRaycastEntityContextId, AzFramework::EntityContextId());
MOCK_CONST_METHOD1(
GetClosestIntersection, AzFramework::RenderGeometry::RayResult(const AzFramework::RenderGeometry::RayRequest&));
};
} // namespace UnitTest
@@ -7,6 +7,7 @@
*/
#include <AzFramework/Render/IntersectorInterface.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
namespace AzToolsFramework
@@ -66,15 +67,19 @@ namespace AzToolsFramework
AZ::Vector3 FindClosestPickIntersection(const AzFramework::RenderGeometry::RayRequest& rayRequest, const float defaultDistance)
{
AzFramework::RenderGeometry::RayResult renderGeometryIntersectionResult;
// attempt a ray intersection with any visible mesh or terrain and return the intersection position if successful
AZ::EBusReduceResult<AzFramework::RenderGeometry::RayResult, AzFramework::RenderGeometry::RayResultClosestAggregator> renderGeometryIntersectionResult;
AzFramework::RenderGeometry::IntersectorBus::EventResult(
renderGeometryIntersectionResult, AzToolsFramework::GetEntityContextId(),
&AzFramework::RenderGeometry::IntersectorBus::Events::RayIntersect, rayRequest);
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
renderGeometryIntersectionResult,
&AzFramework::Terrain::TerrainDataRequests::GetClosestIntersection,
rayRequest);
// attempt a ray intersection with any visible mesh and return the intersection position if successful
if (renderGeometryIntersectionResult)
if (renderGeometryIntersectionResult.value)
{
return renderGeometryIntersectionResult.m_worldPosition;
return renderGeometryIntersectionResult.value.m_worldPosition;
}
else
{
@@ -0,0 +1,392 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <TerrainRaycast/TerrainRaycastContext.h>
#include <TerrainSystem/TerrainSystem.h>
#include <AzCore/Math/IntersectSegment.h>
#include <AzCore/std/sort.h>
using namespace Terrain;
namespace
{
////////////////////////////////////////////////////////////////////////////////////////////////////
// Convenience function to clamp a value to the given grid resolution, rounding up.
inline float ClampToGridRoundUp(float value, float gridResolution)
{
return ceil(value / gridResolution) * gridResolution;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Convenience function to clamp a value to the given grid resolution, rounding down.
inline float ClampToGridRoundDown(float value, float gridResolution)
{
return floor(value / gridResolution) * gridResolution;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Convenience function to find the nearest intersection (if any) between an AABB and a ray.
inline void FindNearestIntersection(const AZ::Aabb& aabb,
const AZ::Vector3& rayStart,
const AZ::Vector3& rayDirection,
const AZ::Vector3& rayDirectionReciprocal,
AzFramework::RenderGeometry::RayResult& result)
{
float intersectionT;
float intersectionEndT;
AZ::Vector3 intersectionNormal;
const int intersectionResult = AZ::Intersect::IntersectRayAABB(rayStart,
rayDirection,
rayDirectionReciprocal,
aabb,
intersectionT,
intersectionEndT,
intersectionNormal);
if (intersectionResult != AZ::Intersect::ISECT_RAY_AABB_NONE)
{
result.m_worldPosition = rayStart + (rayDirection * intersectionT);
result.m_worldNormal = intersectionNormal;
result.m_distance = rayDirection.GetLength() * intersectionT;
}
else
{
result.m_distance = FLT_MAX;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Convenience function to find the nearest intersection (if any) between a triangle and a ray.
// This is an implementation of the Moller-Trumbore intersection algorithm. I first attempted to use
// the existing AZ::Intersect::IntersectSegmentTriangleCCW, which appears to use the same algorithm,
// but it takes a line segment as opposed to a ray and was not returning the expected results. Once
// I've written some tests I can go back and try it again to figure out what is different, but this
// is all likely to get replaced with an optimized SIMD version anyway so this should be ok for now.
inline void FindNearestIntersection(const AZ::Vector3& vertexA,
const AZ::Vector3& vertexB,
const AZ::Vector3& vertexC,
const AZ::Vector3& rayStart,
const AZ::Vector3& rayDirection,
AzFramework::RenderGeometry::RayResult& result)
{
const AZ::Vector3 edgeAB = vertexB - vertexA;
const AZ::Vector3 edgeAC = vertexC - vertexA;
const AZ::Vector3 pVec = rayDirection.Cross(edgeAC);
const float det = edgeAB.Dot(pVec);
if (AZ::IsClose(det, 0.0f))
{
// The ray is parallel to the triangle.
return;
}
const float detInv = 1.0f / det;
const AZ::Vector3 tVec = rayStart - vertexA;
const float u = detInv * tVec.Dot(pVec);
if (u < 0.0f || u > 1.0f)
{
// No intersection.
return;
}
const AZ::Vector3 qVec = tVec.Cross(edgeAB);
const float v = detInv * rayDirection.Dot(qVec);
if (v < 0.0 || u + v > 1.0)
{
// No intersection.
return;
}
const float t = detInv * edgeAC.Dot(qVec);
if (t > FLT_EPSILON)
{
result.m_worldPosition = rayStart + (rayDirection * t);
result.m_worldNormal = edgeAB.Cross(edgeAC);
result.m_distance = rayDirection.GetLength() * t;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Convenience function to get the terrain height values at each corner of an AABB, triangulate them,
// and then find the nearest intersection (if any) between the resulting triangles and the given ray.
inline void TriangulateAndFindNearestIntersection(const TerrainSystem& terrainSystem,
const AZ::Aabb& aabb,
const AZ::Vector3& rayStart,
const AZ::Vector3& rayDirection,
const AZ::Vector3& rayDirectionReciprocal,
AzFramework::RenderGeometry::RayResult& result)
{
// Obtain the height values at each corner of the AABB.
const AZ::Vector3& aabbMin = aabb.GetMin();
const AZ::Vector3& aabbMax = aabb.GetMax();
AZ::Vector3 point0 = aabbMin;
AZ::Vector3 point2 = aabbMax;
AZ::Vector3 point1(point0.GetX(), point2.GetY(), 0.0f);
AZ::Vector3 point3(point2.GetX(), point0.GetY(), 0.0f);
point0.SetZ(terrainSystem.GetHeight(point0, AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT));
point1.SetZ(terrainSystem.GetHeight(point1, AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT));
point2.SetZ(terrainSystem.GetHeight(point2, AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT));
point3.SetZ(terrainSystem.GetHeight(point3, AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT));
// Construct a smaller AABB that tightly encloses the four terrain points.
const float refinedMinZ = AZStd::GetMin(AZStd::GetMin(AZStd::GetMin(point0.GetZ(), point1.GetZ()), point2.GetZ()), point3.GetZ());
const float refinedMaxZ = AZStd::GetMax(AZStd::GetMax(AZStd::GetMax(point0.GetZ(), point1.GetZ()), point2.GetZ()), point3.GetZ());
const AZ::Vector3 refinedMin(aabbMin.GetX(), aabbMin.GetY(), refinedMinZ);
const AZ::Vector3 refinedMax(aabbMax.GetX(), aabbMax.GetY(), refinedMaxZ);
const AZ::Aabb refinedAABB = AZ::Aabb::CreateFromMinMax(refinedMin, refinedMax);
// Check for a hit against the refined AABB.
float intersectionT;
float intersectionEndT;
const int intersectionResult = AZ::Intersect::IntersectRayAABB2(rayStart,
rayDirectionReciprocal,
refinedAABB,
intersectionT,
intersectionEndT);
if (intersectionResult == AZ::Intersect::ISECT_RAY_AABB_NONE)
{
return;
}
// Finally, triangulate the four terrain points and check for a hit,
// splitting using the top-left -> bottom-right diagonal so to match
// the current behavior of the terrain physics and rendering systems.
AzFramework::RenderGeometry::RayResult bottomLeftIntersectionResult;
FindNearestIntersection(rayStart,
rayDirection,
point0,
point3,
point1,
bottomLeftIntersectionResult);
AzFramework::RenderGeometry::RayResult topRightIntersectionResult;
FindNearestIntersection(rayStart,
rayDirection,
point2,
point1,
point3,
topRightIntersectionResult);
if (bottomLeftIntersectionResult)
{
result = !topRightIntersectionResult || bottomLeftIntersectionResult.m_distance < topRightIntersectionResult.m_distance ?
bottomLeftIntersectionResult :
topRightIntersectionResult;
}
else if (topRightIntersectionResult)
{
result = topRightIntersectionResult;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Iterative function that divides an AABB encompasing terrain points into columns (or a voxel grid)
// of size equal to the given grid resolution, steps along the ray visiting each voxel it intersects
// in order from nearest to farthest, then obtains the terrain height values at each corner in order
// to triangulate them and find the nearest intersection (if any) between the triangles and the ray.
//
// Visualization:
// - X: Column intersection but no triangle hit found
// - T: Column intersection with a triangle hit found
// ________________________________________
// | | | | | | | | |
// |____|____|____|____|____|____|____|____| Ray
// | | | | | | | | | /
// |____|____|____|____|____|____|____|____| /
// | | | | | | | | X |/
// |____|____|____|____|____|____|____|____/
// | | | | | | | | X /|
// |____|____|____|____|____|____|____|__/_|
// | | | | | | | | /X |
// |____|____|____|____|____|____|____|/___|
// | | | | | | | X / X |
// |____|____|____|____|____|____|___/|____|
// | | | | | | | T/ | |
// |____|____|____|____|____|____|____|____|
// | | | | | | | | |
// |____|____|____|____|____|____|____|____|
inline void FindNearestIntersectionIterative(const TerrainSystem& terrainSystem,
const AZ::Vector2& terrainResolution,
const AZ::Aabb& terrainWorldBounds,
const AZ::Vector3& rayStart,
const AZ::Vector3& rayEnd,
AzFramework::RenderGeometry::RayResult& result)
{
// Find the nearest intersection (if any) between the ray and terrain world bounds.
// Note that the ray might (and often will) start inside the terrain world bounds.
const AZ::Vector3 rayDirection = rayEnd - rayStart;
const AZ::Vector3 rayDirectionReciprocal = rayDirection.GetReciprocal();
FindNearestIntersection(terrainWorldBounds,
rayStart,
rayDirection,
rayDirectionReciprocal,
result);
if (!result)
{
// The ray does not intersect the terrain world bounds.
return;
}
// The terrain world can be visualized as a grid of columns,
// where the terrain resolution determines the dimensions of
// each column, or a voxel grid with one cell in z dimension.
//
// Starting at the voxel containing the initial intersection,
// we want to step along the ray and visit each voxel the ray
// intersects in order from nearest to furthest until we find
// an intersection with the terrain or the ray exits the grid.
const AZ::Vector3& initialIntersection = result.m_worldPosition;
const float initialIntersectionX = initialIntersection.GetX();
const float initialIntersectionY = initialIntersection.GetY();
const float initialIntersectionZ = initialIntersection.GetZ();
const float gridResolutionX = terrainResolution.GetX();
const float gridResolutionY = terrainResolution.GetY();
const float gridResolutionZ = terrainWorldBounds.GetMax().GetZ() - terrainWorldBounds.GetMin().GetZ();
float initialVoxelMinX = ClampToGridRoundDown(initialIntersectionX, gridResolutionX);
float initialVoxelMinY = ClampToGridRoundDown(initialIntersectionY, gridResolutionY);
float initialVoxelMinZ = terrainWorldBounds.GetMin().GetZ();
float initialVoxelMaxX = ClampToGridRoundUp(initialIntersectionX, gridResolutionX);
float initialVoxelMaxY = ClampToGridRoundUp(initialIntersectionY, gridResolutionY);
float initialVoxelMaxZ = terrainWorldBounds.GetMax().GetZ();
// For each axis calculate the distance t we need to move along
// the ray in order to fully traverse a voxel in that dimension.
const float rayDirectionX = rayDirection.GetX();
const float rayDirectionY = rayDirection.GetY();
const float rayDirectionZ = rayDirection.GetZ();
const float stepX = AZ::GetSign(rayDirectionX) * gridResolutionX;
const float stepY = AZ::GetSign(rayDirectionY) * gridResolutionY;
const float stepZ = AZ::GetSign(rayDirectionZ) * gridResolutionZ;
const float tDeltaX = rayDirectionX ?
stepX / rayDirectionX :
std::numeric_limits<float>::max();
const float tDeltaY = rayDirectionY ?
stepY / rayDirectionY :
std::numeric_limits<float>::max();
const float tDeltaZ = rayDirectionZ ?
stepZ / rayDirectionZ :
std::numeric_limits<float>::max();
// For each axis, calculate the distance t we need to move along the ray
// from the initial intersection point to the next voxel along that axis.
const float offsetX = stepX < 0.0f ?
initialVoxelMinX - initialIntersectionX :
initialVoxelMaxX - initialIntersectionX;
const float offsetY = stepY < 0.0f ?
initialVoxelMinY - initialIntersectionY :
initialVoxelMaxY - initialIntersectionY;
const float offsetZ = stepZ < 0.0f ?
initialVoxelMinZ - initialIntersectionZ :
initialVoxelMaxZ - initialIntersectionZ;
float tMaxX = rayDirectionX ?
offsetX / rayDirectionX :
std::numeric_limits<float>::max();
float tMaxY = rayDirectionY ?
offsetY / rayDirectionY :
std::numeric_limits<float>::max();
float tMaxZ = rayDirectionZ ?
offsetZ / rayDirectionZ :
std::numeric_limits<float>::max();
// Calculate the min/max voxel grid value on each axis by expanding
// the terrain world bounds so they align with the grid resolution.
const float voxelGridMinX = ClampToGridRoundDown(terrainWorldBounds.GetMin().GetX(), gridResolutionX);
const float voxelGridMinY = ClampToGridRoundDown(terrainWorldBounds.GetMin().GetY(), gridResolutionY);
const float voxelGridMinZ = terrainWorldBounds.GetMin().GetZ();
const float voxelGridMaxX = ClampToGridRoundUp(terrainWorldBounds.GetMax().GetX(), gridResolutionX);
const float voxelGridMaxY = ClampToGridRoundUp(terrainWorldBounds.GetMax().GetY(), gridResolutionY);
const float voxelGridMaxZ = terrainWorldBounds.GetMax().GetZ();
// Using the initial voxel values, construct an AABB representing the current voxel,
// then grab references to AABBs min/max vectors so we can manipulate them directly.
AZ::Aabb currentVoxel = AZ::Aabb::CreateFromMinMax({initialVoxelMinX, initialVoxelMinY, initialVoxelMinZ},
{initialVoxelMaxX, initialVoxelMaxY, initialVoxelMaxZ});
AZ::Vector3& currentVoxelMin = const_cast<AZ::Vector3&>(currentVoxel.GetMin());
AZ::Vector3& currentVoxelMax = const_cast<AZ::Vector3&>(currentVoxel.GetMax());
const AZ::Vector3 stepVecX(stepX, 0.0f, 0.0f);
const AZ::Vector3 stepVecY(0.0f, stepY, 0.0f);
const AZ::Vector3 stepVecZ(0.0f, 0.0f, stepZ);
// Now we can step along the ray and visit each voxel the ray
// intersects in order from nearest to furthest until we find
// an intersection with the terrain or the ray exits the grid.
result = AzFramework::RenderGeometry::RayResult();
while (currentVoxel.GetMin().GetX() <= voxelGridMaxX &&
currentVoxel.GetMax().GetX() >= voxelGridMinX &&
currentVoxel.GetMin().GetY() <= voxelGridMaxY &&
currentVoxel.GetMax().GetY() >= voxelGridMinY &&
currentVoxel.GetMin().GetZ() <= voxelGridMaxZ &&
currentVoxel.GetMax().GetZ() >= voxelGridMinZ &&
tMaxX <= 1.0f && tMaxY <= 1.0f && tMaxZ <= 1.0f)
{
TriangulateAndFindNearestIntersection(terrainSystem,
currentVoxel,
rayStart,
rayDirection,
rayDirectionReciprocal,
result);
if (result)
{
// Intersection found.
break;
}
// Step to the next voxel.
if (tMaxX < tMaxY && tMaxX < tMaxZ)
{
currentVoxelMin += stepVecX;
currentVoxelMax += stepVecX;
tMaxX += tDeltaX;
}
else if (tMaxY < tMaxZ)
{
currentVoxelMin += stepVecY;
currentVoxelMax += stepVecY;
tMaxY += tDeltaY;
}
else
{
currentVoxelMin += stepVecZ;
currentVoxelMax += stepVecZ;
tMaxZ += tDeltaZ;
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TerrainRaycastContext::TerrainRaycastContext(TerrainSystem& terrainSystem)
: m_terrainSystem(terrainSystem)
, m_entityContextId(AzFramework::EntityContextId::CreateRandom())
{
AzFramework::RenderGeometry::IntersectorBus::Handler::BusConnect(m_entityContextId);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
TerrainRaycastContext::~TerrainRaycastContext()
{
AzFramework::RenderGeometry::IntersectorBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
AzFramework::RenderGeometry::RayResult TerrainRaycastContext::RayIntersect(
const AzFramework::RenderGeometry::RayRequest& ray)
{
const AZ::Aabb terrainWorldBounds = m_terrainSystem.GetTerrainAabb();
const AZ::Vector2 terrainResolution = m_terrainSystem.GetTerrainHeightQueryResolution();
AzFramework::RenderGeometry::RayResult rayIntersectionResult;
FindNearestIntersectionIterative(m_terrainSystem,
terrainResolution,
terrainWorldBounds,
ray.m_startWorldPosition,
ray.m_endWorldPosition,
rayIntersectionResult);
// If needed we could call m_terrainSystem.FindBestAreaEntityAtPosition in order to set
// rayIntersectionResult.m_entityAndComponent, but I'm not sure whether that is correct.
return rayIntersectionResult;
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/Render/IntersectorInterface.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Terrain
{
class TerrainSystem;
////////////////////////////////////////////////////////////////////////////////////////////////
class TerrainRaycastContext : public AzFramework::RenderGeometry::IntersectorBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] terrainSystem The terrain system that owns this terrain raycast context
TerrainRaycastContext(TerrainSystem& terrainSystem);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(TerrainRaycastContext);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~TerrainRaycastContext();
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the terrain raycast context's entity context id
//! \return The terrain raycast context's entity context id
inline AzFramework::EntityContextId GetEntityContextId() const { return m_entityContextId; }
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::RenderGeometry::RayIntersect
AzFramework::RenderGeometry::RayResult RayIntersect(const AzFramework::RenderGeometry::RayRequest& ray) override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
// RenderGeometry::IntersectorBus inherits from RenderGeometry::IntersectionNotifications,
// so we must override the following pure virtual functions. We could potentially implement
// them using TerrainSystem::m_registeredAreas, but right now that would not serve a purpose.
///@{
//! Unused pure virtual override
void OnEntityConnected(AZ::EntityId) override {}
void OnEntityDisconnected(AZ::EntityId) override {}
void OnGeometryChanged(AZ::EntityId) override {}
///@}
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
TerrainSystem& m_terrainSystem; //!< Terrain system that owns this terrain raycast context
AzFramework::EntityContextId m_entityContextId; //!< This object's entity context id
};
} // namespace Terrain
@@ -51,6 +51,7 @@ bool TerrainLayerPriorityComparator::operator()(const AZ::EntityId& layer1id, co
}
TerrainSystem::TerrainSystem()
: m_terrainRaycastContext(*this)
{
Terrain::TerrainSystemServiceRequestBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
@@ -438,6 +439,16 @@ void TerrainSystem::GetSurfacePointFromFloats(
GetSurfacePoint(AZ::Vector3(x, y, 0.0f), outSurfacePoint, sampleFilter, terrainExistsPtr);
}
AzFramework::EntityContextId TerrainSystem::GetTerrainRaycastEntityContextId() const
{
return m_terrainRaycastContext.GetEntityContextId();
}
AzFramework::RenderGeometry::RayResult TerrainSystem::GetClosestIntersection(
const AzFramework::RenderGeometry::RayRequest& ray) const
{
return m_terrainRaycastContext.RayIntersect(ray);
}
AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::Aabb& bounds) const
{
@@ -23,6 +23,7 @@
#include <AzCore/Jobs/JobFunction.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <TerrainRaycast/TerrainRaycastContext.h>
#include <TerrainSystem/TerrainSystemBus.h>
namespace Terrain
@@ -187,6 +188,9 @@ namespace Terrain
AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override;
AzFramework::EntityContextId GetTerrainRaycastEntityContextId() const override;
AzFramework::RenderGeometry::RayResult GetClosestIntersection(
const AzFramework::RenderGeometry::RayRequest& ray) const override;
private:
void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const;
@@ -230,5 +234,7 @@ namespace Terrain
mutable AZStd::shared_mutex m_areaMutex;
AZStd::map<AZ::EntityId, TerrainAreaData, TerrainLayerPriorityComparator> m_registeredAreas;
mutable TerrainRaycastContext m_terrainRaycastContext;
};
} // namespace Terrain
+2
View File
@@ -27,6 +27,8 @@ set(FILES
Source/Components/TerrainWorldDebuggerComponent.h
Source/Components/TerrainWorldRendererComponent.cpp
Source/Components/TerrainWorldRendererComponent.h
Source/TerrainRaycast/TerrainRaycastContext.cpp
Source/TerrainRaycast/TerrainRaycastContext.h
Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp
Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h
Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp