Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>icon / Environmental / Surface Data</title>
<desc>Created with Sketch.</desc>
<g id="icon-/-Environmental-/-Surface-Data" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M21.1504022,17.0250283 L22.9351306,22.0770469 L16.4632938,22.0770469 L16.1316327,20.0578353 L19.698235,20.0578353 L18.7976296,17.0250283 L21.1504022,17.0250283 Z M3.16484007,17.001392 L5.51761265,17.001392 L4.7567612,20.1085151 L8.56424139,20.1085151 L7.87245518,22.0770469 L1.06486942,22.0770469 L3.16484007,17.001392 Z M10.4834055,14.0030191 L10.7637653,14.6480401 C9.67541949,14.8495321 8.933732,15.2501196 8.933732,15.7114 C8.933732,16.3751943 10.4696146,16.9133059 12.3642218,16.9133059 C14.258829,16.9133059 15.7947116,16.3751943 15.7947116,15.7114 C15.7947116,15.2594563 15.0827451,14.8657735 14.0303362,14.6605187 L14.2964069,14.0030191 L16.1776104,14.0030191 L17.8709488,18.97694 L6.59798017,18.97694 L8.43727058,14.0030191 L10.4834055,14.0030191 Z M20.7168545,14.9545929 L18.2970728,14.9545929 L17.4743641,13.0514453 L20.0296152,13.0514453 L20.7168545,14.9545929 Z M3.72206852,14.9545929 L4.83035055,13.0514453 L7.38560161,13.0514453 L6.14185022,14.9545929 L3.72206852,14.9545929 Z M4.2856351,7.15493845 L5.12956162,7.4959069 L3.01588623,9.36165503 L2.79149296,6.55126584 L3.72665459,6.92909567 C4.39921197,5.40846002 5.44793029,3.60960289 6.39905176,3.10967934 C7.81993244,2.36284336 9.53420368,2.5839193 10.5186736,3.77314285 C11.254953,4.66255634 11.6422812,6.13536824 12.2022328,7.87218297 C12.2022328,5.90802867 12.2778036,3.87076362 13.6958635,2.53160517 C15.2015649,1.10968164 17.4613298,1.24808271 19.0776668,2.23852946 L19.8082418,1.33634448 L20.9744868,3.90315426 L18.221292,3.29606434 L18.8145883,2.56340443 C17.3271124,1.97349977 15.9573931,2.32981632 14.8192094,3.77314285 C13.8679242,4.97946412 13.5218004,7.25683912 13.4144313,9.43732972 C14.2672555,7.29959901 15.329817,6.20527797 16.1166241,5.85432911 C17.9533306,5.0350813 19.7229549,6.77109418 20.1616243,8.88013833 L21.460831,8.74358621 L20.3068849,11.8193051 L18.5386774,9.05071692 L19.6566524,8.93321302 C18.9375592,7.65276929 17.6234316,7.39000755 16.7252772,7.7061777 C14.3292213,8.54964228 14.0552947,11.0169601 12.9878205,15.3062633 C12.9878205,15.3103183 12.1359876,15.3103183 11.9036786,15.3062633 L11.9069618,15.2887223 C11.679267,15.2892791 11.4660593,15.2951261 11.3753538,15.3062633 C11.3753538,14.4823645 11.4929889,14.6752979 11.1704344,13.418079 C10.84788,12.1608601 10.239422,10.5052322 8.69987023,11.4550865 L8.69987023,10.7464725 C10.0026807,9.83397556 10.7272318,10.8221221 11.3753538,11.9728705 C10.9863971,9.83397556 10.2980196,9.27588785 9.64305323,8.47611675 C8.88748351,7.55350014 8.19517876,7.11360366 7.48766824,7.63988233 L8.18215464,8.33436872 L5.50750032,9.22592016 L6.39905176,6.55126584 L7.08548907,7.23770316 C7.68411556,6.46361352 8.53546795,6.23491918 9.57450971,7.06572569 C10.0757529,7.46651433 10.489265,7.84488785 10.8750712,8.36621315 C10.5206001,7.04193732 10.144264,6.12548665 9.74606291,5.61686114 C8.99472791,4.65717488 8.08338892,3.78707723 6.32118079,4.5725635 C5.34730044,5.00666072 4.75549362,6.14014111 4.2856351,7.15493845 L4.2856351,7.15493845 Z" id="Shape" fill="#8BDD15"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66dd1391fc262120cf8f065c86ccdb4444fad93976152635b8091862e3334d5a
size 1474
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>icon / Environmental / Surface Data Copy</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#ADADAD" stop-opacity="0" offset="0%"></stop>
<stop stop-color="#9D9D9D" offset="100%"></stop>
</linearGradient>
<path d="M20.924815,-3.10055012e-14 C23.1021676,-1.21881628e-13 23.9566542,1.77846722 23.9566542,3.01865122 C23.9566542,4.25883521 23.9566542,18.7815894 23.9566542,20.6420628 C23.9566542,22.3929727 22.7281352,23.9566542 20.924815,23.9566542 C19.1214948,23.9566542 4.64929841,23.9566542 2.80929064,23.9566542 C1.38661643,23.9566542 1.16706519e-13,22.5147712 6.91022832e-14,21.1586662 C2.15231996e-14,19.8025612 2.19806118e-14,4.03532882 6.91022832e-14,2.67255351 C1.16426365e-13,1.3097782 1.36539793,-3.10055012e-14 2.60955829,-3.10055012e-14 C3.85371865,-3.10055012e-14 18.9601214,5.12500292e-14 20.924815,-3.10055012e-14 Z" id="path-2"></path>
</defs>
<g id="icon-/-Environmental-/-Surface-Data-Copy" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon/general/color/-box-template">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<use id="Path-3" fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
<g id="icon/general/color/Environmental" style="mix-blend-mode: multiply;" mask="url(#mask-3)" fill="#8BDD15" fill-rule="evenodd">
<rect id="Rectangle-2" x="0" y="0" width="24" height="24"></rect>
</g>
</g>
<path d="M21.1504022,17.0250283 L22.9351306,22.0770469 L16.4632938,22.0770469 L16.1316327,20.0578353 L19.698235,20.0578353 L18.7976296,17.0250283 L21.1504022,17.0250283 Z M3.16484007,17.001392 L5.51761265,17.001392 L4.7567612,20.1085151 L8.56424139,20.1085151 L7.87245518,22.0770469 L1.06486942,22.0770469 L3.16484007,17.001392 Z M10.4834055,14.0030191 L10.7637653,14.6480401 C9.67541949,14.8495321 8.933732,15.2501196 8.933732,15.7114 C8.933732,16.3751943 10.4696146,16.9133059 12.3642218,16.9133059 C14.258829,16.9133059 15.7947116,16.3751943 15.7947116,15.7114 C15.7947116,15.2594563 15.0827451,14.8657735 14.0303362,14.6605187 L14.2964069,14.0030191 L16.1776104,14.0030191 L17.8709488,18.97694 L6.59798017,18.97694 L8.43727058,14.0030191 L10.4834055,14.0030191 Z M20.7168545,14.9545929 L18.2970728,14.9545929 L17.4743641,13.0514453 L20.0296152,13.0514453 L20.7168545,14.9545929 Z M3.72206852,14.9545929 L4.83035055,13.0514453 L7.38560161,13.0514453 L6.14185022,14.9545929 L3.72206852,14.9545929 Z M4.2856351,7.15493845 L5.12956162,7.4959069 L3.01588623,9.36165503 L2.79149296,6.55126584 L3.72665459,6.92909567 C4.39921197,5.40846002 5.44793029,3.60960289 6.39905176,3.10967934 C7.81993244,2.36284336 9.53420368,2.5839193 10.5186736,3.77314285 C11.254953,4.66255634 11.6422812,6.13536824 12.2022328,7.87218297 C12.2022328,5.90802867 12.2778036,3.87076362 13.6958635,2.53160517 C15.2015649,1.10968164 17.4613298,1.24808271 19.0776668,2.23852946 L19.8082418,1.33634448 L20.9744868,3.90315426 L18.221292,3.29606434 L18.8145883,2.56340443 C17.3271124,1.97349977 15.9573931,2.32981632 14.8192094,3.77314285 C13.8679242,4.97946412 13.5218004,7.25683912 13.4144313,9.43732972 C14.2672555,7.29959901 15.329817,6.20527797 16.1166241,5.85432911 C17.9533306,5.0350813 19.7229549,6.77109418 20.1616243,8.88013833 L21.460831,8.74358621 L20.3068849,11.8193051 L18.5386774,9.05071692 L19.6566524,8.93321302 C18.9375592,7.65276929 17.6234316,7.39000755 16.7252772,7.7061777 C14.3292213,8.54964228 14.0552947,11.0169601 12.9878205,15.3062633 C12.9878205,15.3103183 12.1359876,15.3103183 11.9036786,15.3062633 L11.9069618,15.2887223 C11.679267,15.2892791 11.4660593,15.2951261 11.3753538,15.3062633 C11.3753538,14.4823645 11.4929889,14.6752979 11.1704344,13.418079 C10.84788,12.1608601 10.239422,10.5052322 8.69987023,11.4550865 L8.69987023,10.7464725 C10.0026807,9.83397556 10.7272318,10.8221221 11.3753538,11.9728705 C10.9863971,9.83397556 10.2980196,9.27588785 9.64305323,8.47611675 C8.88748351,7.55350014 8.19517876,7.11360366 7.48766824,7.63988233 L8.18215464,8.33436872 L5.50750032,9.22592016 L6.39905176,6.55126584 L7.08548907,7.23770316 C7.68411556,6.46361352 8.53546795,6.23491918 9.57450971,7.06572569 C10.0757529,7.46651433 10.489265,7.84488785 10.8750712,8.36621315 C10.5206001,7.04193732 10.144264,6.12548665 9.74606291,5.61686114 C8.99472791,4.65717488 8.08338892,3.78707723 6.32118079,4.5725635 C5.34730044,5.00666072 4.75549362,6.14014111 4.2856351,7.15493845 L4.2856351,7.15493845 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

+12
View File
@@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
add_subdirectory(Code)
+95
View File
@@ -0,0 +1,95 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
ly_add_target(
NAME SurfaceData.Static STATIC
NAMESPACE Gem
FILES_CMAKE
surfacedata_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
PUBLIC
Gem::LmbrCentral
)
ly_add_target(
NAME SurfaceData ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
NAMESPACE Gem
OUTPUT_NAME Gem.SurfaceData.5de82d29d6094bfe97c1a4d35fcd5fbe.v0.1.0
FILES_CMAKE
surfacedata_shared_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
Gem::SurfaceData.Static
)
if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME SurfaceData.Editor MODULE
NAMESPACE Gem
OUTPUT_NAME Gem.SurfaceData.Editor.5de82d29d6094bfe97c1a4d35fcd5fbe.v0.1.0
FILES_CMAKE
surfacedata_editor_files.cmake
COMPILE_DEFINITIONS
PRIVATE
SURFACEDATA_EDITOR
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
Legacy::CryCommon
AZ::AzToolsFramework
Gem::SurfaceData.Static
)
endif()
################################################################################
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME SurfaceData.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
surfacedata_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
Tests
Source
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Legacy::CryCommon
Gem::SurfaceData.Static
)
ly_add_googletest(
NAME Gem::SurfaceData.Tests
)
endif()
@@ -0,0 +1,36 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
namespace SurfaceData
{
namespace Constants
{
static const char* s_unassignedTagName = "(unassigned)";
static const char* s_terrainHoleTagName = "terrainHole";
static const char* s_terrainTagName = "terrain";
static const AZ::Crc32 s_unassignedTagCrc = AZ::Crc32(s_unassignedTagName);
static const AZ::Crc32 s_terrainHoleTagCrc = AZ::Crc32(s_terrainHoleTagName);
static const AZ::Crc32 s_terrainTagCrc = AZ::Crc32(s_terrainTagName);
static const char* s_allTagNames[] =
{
s_unassignedTagName,
s_terrainHoleTagName,
s_terrainTagName,
};
}
}
@@ -0,0 +1,42 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace SurfaceData
{
/**
* the EBus is used to request information about a surface
*/
class SurfaceDataModifierRequests
: public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////
// EBusTraits
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
typedef AZ::u32 BusIdType;
////////////////////////////////////////////////////////////////////////
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
virtual void ModifySurfacePoints(SurfacePointList& surfacePointList) const = 0;
};
typedef AZ::EBus<SurfaceDataModifierRequests> SurfaceDataModifierRequestBus;
}
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace SurfaceData
{
/**
* the EBus is used to request information about a surface
*/
class SurfaceDataProviderRequests
: public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////
// EBusTraits
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
typedef AZ::u32 BusIdType;
////////////////////////////////////////////////////////////////////////
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
virtual void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const = 0;
};
typedef AZ::EBus<SurfaceDataProviderRequests> SurfaceDataProviderRequestBus;
}
@@ -0,0 +1,45 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/Entity.h>
namespace AZ
{
class Aabb;
}
namespace SurfaceData
{
/**
* the EBus is used to send notification information about surfaces
*/
class SurfaceDataSystemNotifications
: public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////
// EBusTraits
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
virtual void OnSurfaceChanged(const AZ::EntityId& entityId, const AZ::Aabb& oldBounds, const AZ::Aabb& newBounds) = 0;
};
typedef AZ::EBus<SurfaceDataSystemNotifications> SurfaceDataSystemNotificationBus;
}
@@ -0,0 +1,61 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Entity.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector2.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace SurfaceData
{
/**
* the EBus is used to request information about a surface
*/
class SurfaceDataSystemRequests
: public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////
// EBusTraits
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
// Get all surface points located at the inPosition that matches one or more of the desiredTags. Only the XY components of inPosition are used.
virtual void GetSurfacePoints(const AZ::Vector3& inPosition, const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointList) const = 0;
// Get all surface points for every input position within an AABB region. Only the XY dimensions of the AABB region are used.
// The input positions are chosen by starting at the min sides of inRegion and incrementing by stepSize. This method is inclusive
// on the min sides of the AABB, and exclusive on the max sides (i.e. for a box of (0,0) - (4,4), the point (0,0) is included but (4,4) isn't).
virtual void GetSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, const SurfaceTagVector& desiredTags,
SurfacePointListPerPosition& surfacePointListPerPosition) const = 0;
virtual SurfaceDataRegistryHandle RegisterSurfaceDataProvider(const SurfaceDataRegistryEntry& entry) = 0;
virtual void UnregisterSurfaceDataProvider(const SurfaceDataRegistryHandle& handle) = 0;
virtual void UpdateSurfaceDataProvider(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry) = 0;
virtual SurfaceDataRegistryHandle RegisterSurfaceDataModifier(const SurfaceDataRegistryEntry& entry) = 0;
virtual void UnregisterSurfaceDataModifier(const SurfaceDataRegistryHandle& handle) = 0;
virtual void UpdateSurfaceDataModifier(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry) = 0;
// Notify any dependent systems that they need to refresh their surface data for the provided area.
virtual void RefreshSurfaceData(const AZ::Aabb& dirtyArea) = 0;
};
typedef AZ::EBus<SurfaceDataSystemRequests> SurfaceDataSystemRequestBus;
}
@@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace SurfaceData
{
/**
* the EBus is used to request tags
*/
class SurfaceDataTagEnumeratorRequests : public AZ::ComponentBus
{
public:
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
//tags are accumulated from all enumerators so implementers should not clear container
virtual void GetInclusionSurfaceTags([[maybe_unused]] SurfaceTagVector& tags, [[maybe_unused]] bool& includeAll) const {}
virtual void GetExclusionSurfaceTags([[maybe_unused]] SurfaceTagVector& tags) const {}
};
typedef AZ::EBus<SurfaceDataTagEnumeratorRequests> SurfaceDataTagEnumeratorRequestBus;
}
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace SurfaceData
{
/**
* the EBus is used to request registered tags
*/
class SurfaceDataTagProviderRequests
: public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////
// EBusTraits
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////
//! allows multiple threads to call
using MutexType = AZStd::recursive_mutex;
virtual void GetRegisteredSurfaceTagNames(SurfaceTagNameSet& names) const = 0;
};
typedef AZ::EBus<SurfaceDataTagProviderRequests> SurfaceDataTagProviderRequestBus;
}
@@ -0,0 +1,52 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_set.h>
#include <SurfaceData/SurfaceTag.h>
namespace SurfaceData
{
//map of id or crc to contribution factor
using SurfaceTagWeightMap = AZStd::unordered_map<AZ::Crc32, float>;
using SurfaceTagNameSet = AZStd::unordered_set<AZStd::string>;
using SurfaceTagVector = AZStd::vector<SurfaceTag>;
struct SurfacePoint final
{
AZ_CLASS_ALLOCATOR(SurfacePoint, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(SurfacePoint, "{0DC7E720-68D6-47D4-BB6D-B89EF23C5A5C}");
AZ::EntityId m_entityId;
AZ::Vector3 m_position;
AZ::Vector3 m_normal;
SurfaceTagWeightMap m_masks;
};
using SurfacePointList = AZStd::vector<SurfacePoint>;
using SurfacePointListPerPosition = AZStd::vector<AZStd::pair<AZ::Vector3, SurfacePointList>>;
struct SurfaceDataRegistryEntry
{
AZ::EntityId m_entityId;
AZ::Aabb m_bounds = AZ::Aabb::CreateNull();
SurfaceTagVector m_tags;
};
using SurfaceDataRegistryHandle = AZ::u32;
const SurfaceDataRegistryHandle InvalidSurfaceDataRegistryHandle = 0;
}
@@ -0,0 +1,85 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <SurfaceData/SurfaceDataConstants.h>
namespace SurfaceData
{
/**
* Represents a tag value to match with surface materials and/or masks
*/
class SurfaceTag final
{
public:
AZ_CLASS_ALLOCATOR(SurfaceTag, AZ::SystemAllocator, 0);
AZ_RTTI(SurfaceTag, "{67C8C6ED-F32A-443E-A777-1CAE48B22CD7}");
static void Reflect(AZ::ReflectContext* context);
SurfaceTag()
: m_surfaceTagCrc(Constants::s_unassignedTagCrc)
{
}
SurfaceTag(const AZStd::string& value)
: m_surfaceTagCrc(AZ::Crc32(value.data()))
{
}
SurfaceTag(const AZ::Crc32& value)
: m_surfaceTagCrc(value)
{
}
AZ_INLINE bool operator==(const SurfaceTag& other) const;
AZ_INLINE bool operator<(const SurfaceTag& other) const;
AZ_INLINE operator AZ::Crc32() const;
AZ_INLINE operator AZ::u32() const;
void SetTag(const AZStd::string& value)
{
m_surfaceTagCrc = AZ::Crc32(value.data());
}
static AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> GetRegisteredTags();
private:
bool FindDisplayName(const AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>>& selectableTags, AZStd::string& name) const;
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> BuildSelectableTagList() const;
AZStd::string GetDisplayName() const;
AZ::u32 m_surfaceTagCrc;
};
AZ_INLINE bool SurfaceTag::operator==(const SurfaceTag& other) const
{
return other.m_surfaceTagCrc == m_surfaceTagCrc;
}
AZ_INLINE bool SurfaceTag::operator<(const SurfaceTag& other) const
{
return other.m_surfaceTagCrc < m_surfaceTagCrc;
}
AZ_INLINE SurfaceTag::operator AZ::Crc32() const
{
return m_surfaceTagCrc;
}
AZ_INLINE SurfaceTag::operator AZ::u32() const
{
return static_cast<AZ::u32>(m_surfaceTagCrc);
}
}
@@ -0,0 +1,317 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzTest/AzTest.h>
#include <AzCore/std/hash.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TransformBus.h>
#include <LmbrCentral/Shape/ShapeComponentBus.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <AzCore/Casting/lossy_cast.h>
namespace UnitTest
{
struct SurfaceDataTest
: public ::testing::Test
{
protected:
AZ::ComponentApplication m_app;
AZ::Entity* m_systemEntity = nullptr;
void SetUp() override
{
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 128 * 1024 * 1024;
m_systemEntity = m_app.Create(appDesc);
m_app.AddEntity(m_systemEntity);
}
void TearDown() override
{
m_app.Destroy();
m_systemEntity = nullptr;
}
AZStd::unique_ptr<AZ::Entity> CreateEntity()
{
return AZStd::make_unique<AZ::Entity>();
}
void ActivateEntity(AZ::Entity* entity)
{
entity->Init();
EXPECT_EQ(AZ::Entity::State::Init, entity->GetState());
entity->Activate();
EXPECT_EQ(AZ::Entity::State::Active, entity->GetState());
}
template <typename Component, typename Configuration>
AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config)
{
m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>(config);
}
template <typename Component>
AZ::Component* CreateComponent(AZ::Entity* entity)
{
m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>();
}
};
struct MockShapeComponentHandler
: public LmbrCentral::ShapeComponentRequestsBus::Handler
{
MockShapeComponentHandler(const AZ::EntityId& id)
{
BusConnect(id);
}
~MockShapeComponentHandler() override
{
BusDisconnect();
}
AZ::Aabb m_GetLocalBounds = AZ::Aabb::CreateCenterRadius(AZ::Vector3::CreateZero(), 0.5f);
AZ::Transform m_GetTransform = AZ::Transform::CreateIdentity();
void GetTransformAndLocalBounds(AZ::Transform& transform, AZ::Aabb& bounds) override
{
transform = m_GetTransform;
bounds = m_GetLocalBounds;
}
AZ::Crc32 m_GetShapeType = AZ_CRC("MockShapeComponentHandler", 0x5189d279);
AZ::Crc32 GetShapeType() override
{
return m_GetShapeType;
}
AZ::Aabb m_GetEncompassingAabb = AZ::Aabb::CreateCenterRadius(AZ::Vector3::CreateZero(), 0.5f);
AZ::Aabb GetEncompassingAabb() override
{
return m_GetEncompassingAabb;
}
bool IsPointInside(const AZ::Vector3& point) override
{
return m_GetEncompassingAabb.Contains(point);
}
float DistanceSquaredFromPoint(const AZ::Vector3& point) override
{
return m_GetEncompassingAabb.GetDistanceSq(point);
}
};
struct MockShapeComponent
: public AZ::Component
{
public:
AZ_COMPONENT(MockShapeComponent, "{DD9590BC-916B-4EFA-98B8-AC5023941672}", AZ::Component);
void Activate() override {}
void Deactivate() override {}
static void Reflect(AZ::ReflectContext* reflect) { AZ_UNUSED(reflect); }
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ShapeService", 0xe86aa5fe));
}
};
struct MockTransformHandler
: public AZ::TransformBus::Handler
{
~MockTransformHandler()
{
AZ::TransformBus::Handler::BusDisconnect();
}
AZ::Transform m_GetLocalTMOutput = AZ::Transform::CreateIdentity();
const AZ::Transform & GetLocalTM() override
{
return m_GetLocalTMOutput;
}
AZ::Transform m_GetWorldTMOutput = AZ::Transform::CreateIdentity();
const AZ::Transform & GetWorldTM() override
{
return m_GetWorldTMOutput;
}
bool IsStaticTransform() override
{
return false;
}
bool IsPositionInterpolated() override
{
return false;
}
bool IsRotationInterpolated() override
{
return false;
}
};
struct MockSurfaceDataSystem
: public SurfaceData::SurfaceDataSystemRequestBus::Handler
{
MockSurfaceDataSystem()
{
BusConnect();
}
~MockSurfaceDataSystem()
{
BusDisconnect();
}
AZStd::unordered_map<AZStd::pair<float, float>, SurfaceData::SurfacePointList> m_GetSurfacePoints;
void GetSurfacePoints(const AZ::Vector3& inPosition, [[maybe_unused]] const SurfaceData::SurfaceTagVector& masks, SurfaceData::SurfacePointList& surfacePointList) const override
{
auto surfacePoints = m_GetSurfacePoints.find(AZStd::make_pair(inPosition.GetX(), inPosition.GetY()));
if (surfacePoints != m_GetSurfacePoints.end())
{
surfacePointList = surfacePoints->second;
}
}
void GetSurfacePointsFromRegion([[maybe_unused]] const AZ::Aabb& inRegion, [[maybe_unused]] const AZ::Vector2 stepSize, [[maybe_unused]] const SurfaceData::SurfaceTagVector& desiredTags,
[[maybe_unused]] SurfaceData::SurfacePointListPerPosition& surfacePointListPerPosition) const override
{
}
SurfaceData::SurfaceDataRegistryHandle RegisterSurfaceDataProvider(const SurfaceData::SurfaceDataRegistryEntry& entry) override
{
return RegisterEntry(entry, m_providers);
}
void UnregisterSurfaceDataProvider(const SurfaceData::SurfaceDataRegistryHandle& handle) override
{
UnregisterEntry(handle, m_providers);
}
SurfaceData::SurfaceDataRegistryHandle RegisterSurfaceDataModifier(const SurfaceData::SurfaceDataRegistryEntry& entry) override
{
return RegisterEntry(entry, m_modifiers);
}
void UnregisterSurfaceDataModifier(const SurfaceData::SurfaceDataRegistryHandle& handle) override
{
UnregisterEntry(handle, m_modifiers);
}
void UpdateSurfaceDataModifier(const SurfaceData::SurfaceDataRegistryHandle& handle, const SurfaceData::SurfaceDataRegistryEntry& entry) override
{
UpdateEntry(handle, entry, m_providers);
}
void UpdateSurfaceDataProvider(const SurfaceData::SurfaceDataRegistryHandle& handle, const SurfaceData::SurfaceDataRegistryEntry& entry) override
{
UpdateEntry(handle, entry, m_modifiers);
}
void RefreshSurfaceData([[maybe_unused]] const AZ::Aabb& dirtyBounds) override
{
}
SurfaceData::SurfaceDataRegistryHandle GetSurfaceProviderHandle(AZ::EntityId id)
{
return GetEntryHandle(id, m_providers);
}
SurfaceData::SurfaceDataRegistryHandle GetSurfaceModifierHandle(AZ::EntityId id)
{
return GetEntryHandle(id, m_modifiers);
}
SurfaceData::SurfaceDataRegistryEntry GetSurfaceProviderEntry(AZ::EntityId id)
{
return GetEntry(id, m_providers);
}
SurfaceData::SurfaceDataRegistryEntry GetSurfaceModifierEntry(AZ::EntityId id)
{
return GetEntry(id, m_modifiers);
}
protected:
AZStd::vector<SurfaceData::SurfaceDataRegistryEntry> m_providers;
AZStd::vector<SurfaceData::SurfaceDataRegistryEntry> m_modifiers;
SurfaceData::SurfaceDataRegistryHandle RegisterEntry(const SurfaceData::SurfaceDataRegistryEntry& entry, AZStd::vector<SurfaceData::SurfaceDataRegistryEntry>& entryList)
{
// Keep a list of registered entries. Use the "list index + 1" as the handle. (We add +1 because 0 is used to mean "invalid handle")
entryList.emplace_back(entry);
return entryList.size();
}
void UnregisterEntry(const SurfaceData::SurfaceDataRegistryHandle& handle, AZStd::vector<SurfaceData::SurfaceDataRegistryEntry>& entryList)
{
// We don't actually remove the entry from our list because we use handles as indices, so the indices can't change.
// Clearing out the entity Id should be good enough.
uint32 index = static_cast<uint32>(handle) - 1;
if (index < entryList.size())
{
entryList[index].m_entityId = AZ::EntityId();
}
}
void UpdateEntry(const SurfaceData::SurfaceDataRegistryHandle& handle, const SurfaceData::SurfaceDataRegistryEntry& entry,
AZStd::vector<SurfaceData::SurfaceDataRegistryEntry>& entryList)
{
uint32 index = static_cast<uint32>(handle) - 1;
if (index < entryList.size())
{
entryList[index] = entry;
}
}
SurfaceData::SurfaceDataRegistryHandle GetEntryHandle(AZ::EntityId id, const AZStd::vector<SurfaceData::SurfaceDataRegistryEntry>& entryList)
{
// Look up the requested entity Id and see if we have a registered surface entry with that handle. If so, return the handle.
auto result = AZStd::find_if(entryList.begin(), entryList.end(), [this, id](const SurfaceData::SurfaceDataRegistryEntry& entry) { return entry.m_entityId == id; });
if (result == entryList.end())
{
return SurfaceData::InvalidSurfaceDataRegistryHandle;
}
else
{
// We use "index + 1" as our handle
return static_cast<SurfaceData::SurfaceDataRegistryHandle>(result - entryList.begin() + 1);
}
}
SurfaceData::SurfaceDataRegistryEntry GetEntry(AZ::EntityId id, const AZStd::vector<SurfaceData::SurfaceDataRegistryEntry>& entryList)
{
SurfaceData::SurfaceDataRegistryHandle handle = GetEntryHandle(id, entryList);
if (handle != SurfaceData::InvalidSurfaceDataRegistryHandle)
{
return entryList[handle - 1];
}
else
{
SurfaceData::SurfaceDataRegistryEntry emptyEntry;
return emptyEntry;
}
}
};
}
@@ -0,0 +1,261 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SurfaceData/SurfaceDataTypes.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/IntersectSegment.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
#include <LmbrCentral/Rendering/MeshAsset.h>
#include <IStatObj.h>
#include <MathConversion.h>
namespace SurfaceData
{
AZ_INLINE bool GetQuadListRayIntersection(
const AZStd::vector<AZ::Vector3>& vertices,
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
const float& rayMaxRange,
AZ::Vector3& outPosition,
AZ::Vector3& outNormal)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
const size_t vertexCount = vertices.size();
if (vertexCount > 0 && vertexCount % 4 == 0)
{
// Make sure our raycast segment is at least 1 mm long. If we have a 0-length ray, we'll never intersect.
const float adjustedMaxRange = AZStd::max(0.001f, rayMaxRange);
const AZ::Vector3 rayLength = rayDirection * adjustedMaxRange;
const AZ::Vector3 rayEnd = rayOrigin + rayLength;
for (size_t vertexIndex = 0; vertexIndex < vertexCount; vertexIndex += 4)
{
// This could potentially be optimized further with a single segment / quad intersection check.
// Unfortunately, AZ::Intersect::IntersectRayQuad() currently returns different
// (and worse) results than IntersectSegmentTriangle. It might be that our surface
// quads aren't actually planar, or it might just be a precision or winding order issue.
float resultDistance = 0.0f;
if (AZ::Intersect::IntersectSegmentTriangle(
rayOrigin,
rayEnd,
vertices[vertexIndex + 0],
vertices[vertexIndex + 2],
vertices[vertexIndex + 3],
outNormal,
resultDistance))
{
outPosition = rayOrigin + (rayLength * resultDistance);
return true;
}
resultDistance = 0.0f;
if (AZ::Intersect::IntersectSegmentTriangle(
rayOrigin,
rayEnd,
vertices[vertexIndex + 0],
vertices[vertexIndex + 3],
vertices[vertexIndex + 1],
outNormal,
resultDistance))
{
outPosition = rayOrigin + (rayLength * resultDistance);
return true;
}
}
}
return false;
}
AZ_INLINE bool GetMeshRayIntersection(
const LmbrCentral::MeshAsset& meshAsset,
const AZ::Transform& meshTransform,
const AZ::Transform& meshTransformInverse,
const AZ::Vector3& rayOrigin,
const AZ::Vector3& rayDirection,
AZ::Vector3& outPosition,
AZ::Vector3& outNormal)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
IStatObj* pStatObj = meshAsset.m_statObj.get();
if (pStatObj)
{
const Vec3 rayOriginLocal = AZVec3ToLYVec3(meshTransformInverse.TransformPoint(rayOrigin));
const Vec3 rayDirectionLocal = AZVec3ToLYVec3(meshTransformInverse.TransformVector(rayDirection).GetNormalized());
SRayHitInfo hitInfo = {};
hitInfo.inReferencePoint = rayOriginLocal;
hitInfo.inRay = Ray(rayOriginLocal, rayDirectionLocal);
if (pStatObj->RayIntersection(hitInfo))
{
outPosition = meshTransform.TransformPoint(LYVec3ToAZVec3(hitInfo.vHitPos));
outNormal = meshTransform.TransformVector(LYVec3ToAZVec3(hitInfo.vHitNormal)).GetNormalized();
return true;
}
}
return false;
}
AZ_INLINE void AddMaxValueForMasks(SurfaceTagWeightMap& masks, const AZ::Crc32 tag, const float value)
{
const auto maskItr = masks.find(tag);
const float valueOld = maskItr != masks.end() ? maskItr->second : 0.0f;
masks[tag] = AZ::GetMax(value, valueOld);
}
AZ_INLINE void AddMaxValueForMasks(SurfaceTagWeightMap& masks, const SurfaceTagVector& tags, const float value)
{
for (const auto& tag : tags)
{
AddMaxValueForMasks(masks, tag, value);
}
}
AZ_INLINE void AddMaxValueForMasks(SurfaceTagWeightMap& outMasks, const SurfaceTagWeightMap& inMasks)
{
for (const auto& inMask : inMasks)
{
AddMaxValueForMasks(outMasks, inMask.first, inMask.second);
}
}
template<typename Container, typename Element>
AZ_INLINE void AddItemIfNotFound(Container& container, const Element& element)
{
if (AZStd::find(container.begin(), container.end(), element) == container.end())
{
container.insert(container.end(), element);
}
}
template<typename SourceContainer>
AZ_INLINE bool HasMatchingTag(const SourceContainer& sourceTags, const AZ::Crc32& sampleTag)
{
return AZStd::find(sourceTags.begin(), sourceTags.end(), sampleTag) != sourceTags.end();
}
template<typename SourceContainer, typename SampleContainer>
AZ_INLINE bool HasMatchingTags(const SourceContainer& sourceTags, const SampleContainer& sampleTags)
{
for (const auto& sampleTag : sampleTags)
{
if (HasMatchingTag(sourceTags, sampleTag))
{
return true;
}
}
return false;
}
AZ_INLINE bool HasMatchingTag(const SurfaceTagWeightMap& sourceTags, const AZ::Crc32& sampleTag)
{
return sourceTags.find(sampleTag) != sourceTags.end();
}
template<typename SampleContainer>
AZ_INLINE bool HasMatchingTags(const SurfaceTagWeightMap& sourceTags, const SampleContainer& sampleTags)
{
for (const auto& sampleTag : sampleTags)
{
if (HasMatchingTag(sourceTags, sampleTag))
{
return true;
}
}
return false;
}
AZ_INLINE bool HasMatchingTag(const SurfaceTagWeightMap& sourceTags, const AZ::Crc32& sampleTag, float valueMin, float valueMax)
{
auto maskItr = sourceTags.find(sampleTag);
return maskItr != sourceTags.end() && valueMin <= maskItr->second && valueMax >= maskItr->second;
}
template<typename SampleContainer>
AZ_INLINE bool HasMatchingTags(const SurfaceTagWeightMap& sourceTags, const SampleContainer& sampleTags, float valueMin, float valueMax)
{
for (const auto& sampleTag : sampleTags)
{
if (HasMatchingTag(sourceTags, sampleTag, valueMin, valueMax))
{
return true;
}
}
return false;
}
template<typename SourceContainer>
AZ_INLINE void RemoveUnassignedTags(const SourceContainer& sourceTags)
{
sourceTags.erase(AZStd::remove(sourceTags.begin(), sourceTags.end(), Constants::s_unassignedTagCrc), sourceTags.end());
}
template<typename SourceContainer>
AZ_INLINE bool HasValidTags(const SourceContainer& sourceTags)
{
for (const auto& sourceTag : sourceTags)
{
if (sourceTag != Constants::s_unassignedTagCrc)
{
return true;
}
}
return false;
}
AZ_INLINE bool HasValidTags(const SurfaceTagWeightMap& sourceTags)
{
for (const auto& sourceTag : sourceTags)
{
if (sourceTag.first != Constants::s_unassignedTagCrc)
{
return true;
}
}
return false;
}
// Utility method to compare two AABBs for overlapping XY coordinates while ignoring the Z coordinates.
AZ_INLINE bool AabbOverlaps2D(const AZ::Aabb& box1, const AZ::Aabb& box2)
{
return box1.GetMin().GetX() <= box2.GetMax().GetX() &&
box1.GetMin().GetY() <= box2.GetMax().GetY() &&
box1.GetMax().GetX() >= box2.GetMin().GetX() &&
box1.GetMax().GetY() >= box2.GetMin().GetY();
}
// Utility method to compare an AABB and a point for overlapping XY coordinates while ignoring the Z coordinates.
AZ_INLINE bool AabbContains2D(const AZ::Aabb& box, const AZ::Vector2& point)
{
return box.GetMin().GetX() <= point.GetX() &&
box.GetMin().GetY() <= point.GetY() &&
box.GetMax().GetX() >= point.GetX() &&
box.GetMax().GetY() >= point.GetY();
}
// Utility method to compare an AABB and a point for overlapping XY coordinates while ignoring the Z coordinates.
AZ_INLINE bool AabbContains2D(const AZ::Aabb& box, const AZ::Vector3& point)
{
return box.GetMin().GetX() <= point.GetX() &&
box.GetMin().GetY() <= point.GetY() &&
box.GetMax().GetX() >= point.GetX() &&
box.GetMax().GetY() >= point.GetY();
}
}
@@ -0,0 +1,380 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensor's.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "SurfaceDataColliderComponent.h"
#include <AzCore/Debug/Profiler.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/IntersectSegment.h>
#include <AzFramework/Physics/Casts.h>
#include <AzFramework/Physics/World.h>
#include <AzFramework/Physics/WorldBodyBus.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <SurfaceData/Utility/SurfaceDataUtility.h>
namespace SurfaceData
{
void SurfaceDataColliderConfig::Reflect(AZ::ReflectContext* context)
{
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<SurfaceDataColliderConfig, AZ::ComponentConfig>()
->Version(0)
->Field("ProviderTags", &SurfaceDataColliderConfig::m_providerTags)
->Field("ModifierTags", &SurfaceDataColliderConfig::m_modifierTags)
;
if (auto edit = serialize->GetEditContext())
{
edit->Class<SurfaceDataColliderConfig>(
"PhysX Collider Surface Tag Emitter", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &SurfaceDataColliderConfig::m_providerTags, "Generated Tags", "Surface tags to add to created points")
->DataElement(0, &SurfaceDataColliderConfig::m_modifierTags, "Extended Tags", "Surface tags to add to contained points")
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<SurfaceDataColliderConfig>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Attribute(AZ::Script::Attributes::Module, "surface_data")
->Constructor()
->Property("providerTags", BehaviorValueProperty(&SurfaceDataColliderConfig::m_providerTags))
->Property("modifierTags", BehaviorValueProperty(&SurfaceDataColliderConfig::m_modifierTags))
;
}
}
void SurfaceDataColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
services.push_back(AZ_CRC("SurfaceDataModifierService", 0x68f8aa72));
}
void SurfaceDataColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
services.push_back(AZ_CRC("SurfaceDataModifierService", 0x68f8aa72));
}
void SurfaceDataColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("PhysXColliderService", 0x4ff43f7c));
}
void SurfaceDataColliderComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("PhysicsWorldBodyService", 0x944da0cc));
}
void SurfaceDataColliderComponent::Reflect(AZ::ReflectContext* context)
{
SurfaceDataColliderConfig::Reflect(context);
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<SurfaceDataColliderComponent, AZ::Component>()
->Version(0)
->Field("Configuration", &SurfaceDataColliderComponent::m_configuration)
;
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<SurfaceDataColliderComponent>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Attribute(AZ::Script::Attributes::Module, "surface_data")
->Constructor()
->Property("providerTags",
[](SurfaceDataColliderComponent* component) { return component->m_configuration.m_providerTags; },
[](SurfaceDataColliderComponent* component, SurfaceData::SurfaceTagVector value)
{
component->m_configuration.m_providerTags = value;
component->OnCompositionChanged();
})
->Property("modifierTags",
[](SurfaceDataColliderComponent* component) { return component->m_configuration.m_modifierTags; },
[](SurfaceDataColliderComponent* component, SurfaceData::SurfaceTagVector value)
{
component->m_configuration.m_modifierTags = value;
component->OnCompositionChanged();
})
;
}
}
SurfaceDataColliderComponent::SurfaceDataColliderComponent(const SurfaceDataColliderConfig& configuration)
: m_configuration(configuration)
{
}
void SurfaceDataColliderComponent::Activate()
{
m_providerHandle = InvalidSurfaceDataRegistryHandle;
m_modifierHandle = InvalidSurfaceDataRegistryHandle;
m_refresh = false;
AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
Physics::ColliderComponentEventBus::Handler::BusConnect(GetEntityId());
// Update the cached collider data and bounds, then register the surface data provider / modifier
UpdateColliderData();
}
void SurfaceDataColliderComponent::Deactivate()
{
if (m_providerHandle != InvalidSurfaceDataRegistryHandle)
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
}
if (m_modifierHandle != InvalidSurfaceDataRegistryHandle)
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataModifier, m_modifierHandle);
m_modifierHandle = InvalidSurfaceDataRegistryHandle;
}
AZ::TickBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
Physics::ColliderComponentEventBus::Handler::BusDisconnect();
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
SurfaceDataModifierRequestBus::Handler::BusDisconnect();
m_refresh = false;
// Clear the cached mesh data
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
m_colliderBounds = AZ::Aabb::CreateNull();
}
}
bool SurfaceDataColliderComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const SurfaceDataColliderConfig*>(baseConfig))
{
m_configuration = *config;
return true;
}
return false;
}
bool SurfaceDataColliderComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<SurfaceDataColliderConfig*>(outBaseConfig))
{
*config = m_configuration;
return true;
}
return false;
}
bool SurfaceDataColliderComponent::DoRayTrace(const AZ::Vector3& inPosition, bool queryPointOnly, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
// test AABB as first pass to claim the point
const AZ::Vector3 testPosition = AZ::Vector3(
inPosition.GetX(),
inPosition.GetY(),
m_colliderBounds.GetCenter().GetZ());
if (!m_colliderBounds.Contains(testPosition))
{
return false;
}
Physics::RayCastRequest request;
request.m_direction = AZ::Vector3(0.0f, 0.0f, -1.0f);
if (queryPointOnly)
{
// We're checking to see if the point is *inside* the collider, so give it a distance of 0.
request.m_start = inPosition;
request.m_distance = 0.0f;
}
else
{
// We're casting the ray to look for a collision, so start at the top of the collider and cast downwards
// the full height of the collider.
request.m_start = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_colliderBounds.GetMax().GetZ());
request.m_distance = m_colliderBounds.GetExtents().GetZ();
}
Physics::RayCastHit result;
Physics::WorldBodyRequestBus::EventResult(result, GetEntityId(), &Physics::WorldBodyRequestBus::Events::RayCast, request);
if (result)
{
outPosition = result.m_position;
outNormal = result.m_normal;
return true;
}
return false;
}
void SurfaceDataColliderComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{
AZ::Vector3 hitPosition;
AZ::Vector3 hitNormal;
// We want a full raycast, so don't just query the start point.
constexpr bool queryPointOnly = false;
if (DoRayTrace(inPosition, queryPointOnly, hitPosition, hitNormal))
{
SurfacePoint point;
point.m_entityId = GetEntityId();
point.m_position = hitPosition;
point.m_normal = hitNormal;
AddMaxValueForMasks(point.m_masks, m_configuration.m_providerTags, 1.0f);
surfacePointList.push_back(point);
}
}
void SurfaceDataColliderComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
if (m_colliderBounds.IsValid() && !m_configuration.m_modifierTags.empty())
{
const AZ::EntityId entityId = GetEntityId();
for (auto& point : surfacePointList)
{
if (point.m_entityId != entityId && m_colliderBounds.Contains(point.m_position))
{
AZ::Vector3 hitPosition;
AZ::Vector3 hitNormal;
constexpr bool queryPointOnly = true;
if (DoRayTrace(point.m_position, queryPointOnly, hitPosition, hitNormal))
{
AddMaxValueForMasks(point.m_masks, m_configuration.m_modifierTags, 1.0f);
}
}
}
}
}
void SurfaceDataColliderComponent::OnCompositionChanged()
{
if (!m_refresh)
{
m_refresh = true;
AZ::TickBus::Handler::BusConnect();
}
}
void SurfaceDataColliderComponent::OnColliderChanged()
{
OnCompositionChanged();
}
void SurfaceDataColliderComponent::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, [[maybe_unused]] const AZ::Transform& world)
{
OnCompositionChanged();
}
void SurfaceDataColliderComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if (m_refresh)
{
UpdateColliderData();
m_refresh = false;
}
AZ::TickBus::Handler::BusDisconnect();
}
void SurfaceDataColliderComponent::UpdateColliderData()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
bool colliderValidBeforeUpdate = false;
bool colliderValidAfterUpdate = false;
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
colliderValidBeforeUpdate = m_colliderBounds.IsValid();
m_colliderBounds = AZ::Aabb::CreateNull();
Physics::WorldBodyRequestBus::EventResult(m_colliderBounds, GetEntityId(), &Physics::WorldBodyRequestBus::Events::GetAabb);
colliderValidAfterUpdate = m_colliderBounds.IsValid();
}
SurfaceDataRegistryEntry providerRegistryEntry;
providerRegistryEntry.m_entityId = GetEntityId();
providerRegistryEntry.m_bounds = m_colliderBounds;
providerRegistryEntry.m_tags = m_configuration.m_providerTags;
SurfaceDataRegistryEntry modifierRegistryEntry(providerRegistryEntry);
modifierRegistryEntry.m_tags = m_configuration.m_modifierTags;
if (!colliderValidBeforeUpdate && !colliderValidAfterUpdate)
{
// We didn't have a valid collider before or after running this, so do nothing.
}
else if (!colliderValidBeforeUpdate && colliderValidAfterUpdate)
{
// Our collider has become valid, so register as a provider and save off the provider handle
AZ_Assert((m_providerHandle == InvalidSurfaceDataRegistryHandle), "Surface data handle is initialized before our collider became valid");
AZ_Assert((m_modifierHandle == InvalidSurfaceDataRegistryHandle), "Surface Modifier data handle is initialized before our collider became valid");
AZ_Assert(m_colliderBounds.IsValid(), "Collider Geometry isn't correctly initialized.");
SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, providerRegistryEntry);
SurfaceDataSystemRequestBus::BroadcastResult(m_modifierHandle, &SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataModifier, modifierRegistryEntry);
// Start listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
AZ_Assert((m_modifierHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle);
SurfaceDataModifierRequestBus::Handler::BusConnect(m_modifierHandle);
}
else if (colliderValidBeforeUpdate && !colliderValidAfterUpdate)
{
// Our collider has stopped being valid, so unregister and stop listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
AZ_Assert((m_modifierHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataModifier, m_modifierHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
m_modifierHandle = InvalidSurfaceDataRegistryHandle;
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
SurfaceDataModifierRequestBus::Handler::BusDisconnect();
}
else if (colliderValidBeforeUpdate && colliderValidAfterUpdate)
{
// Our collider was valid before and after, it just changed in some way, so update our registry entry.
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
AZ_Assert((m_modifierHandle != InvalidSurfaceDataRegistryHandle), "Invalid modifier data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UpdateSurfaceDataProvider, m_providerHandle, providerRegistryEntry);
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UpdateSurfaceDataModifier, m_modifierHandle, modifierRegistryEntry);
}
}
}
@@ -0,0 +1,108 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Physics/ColliderComponentBus.h>
#include <SurfaceData/SurfaceDataTypes.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <SurfaceData/SurfaceDataModifierRequestBus.h>
namespace LmbrCentral
{
template<typename, typename>
class EditorWrappedComponentBase;
}
namespace SurfaceData
{
class SurfaceDataColliderConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(SurfaceDataColliderConfig, AZ::SystemAllocator, 0);
AZ_RTTI(SurfaceDataColliderConfig, "{D435DDB9-C513-4A2E-B0AC-9933E9360857}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
SurfaceTagVector m_providerTags;
SurfaceTagVector m_modifierTags;
};
class SurfaceDataColliderComponent
: public AZ::Component
, public AZ::TickBus::Handler
, public AZ::TransformNotificationBus::Handler
, public SurfaceDataProviderRequestBus::Handler
, private SurfaceDataModifierRequestBus::Handler
, public Physics::ColliderComponentEventBus::Handler
{
public:
template<typename, typename> friend class LmbrCentral::EditorWrappedComponentBase;
AZ_COMPONENT(SurfaceDataColliderComponent, "{8BECC930-9B2A-442D-A291-8A3F6B6D1071}");
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void Reflect(AZ::ReflectContext* context);
SurfaceDataColliderComponent(const SurfaceDataColliderConfig& configuration);
SurfaceDataColliderComponent() = default;
~SurfaceDataColliderComponent() = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
//////////////////////////////////////////////////////////////////////////
// ColliderComponentEventBus
// For physics meshes only
void OnColliderChanged() override;
//////////////////////////////////////////////////////////////////////////
// TransformNotificationBus
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
////////////////////////////////////////////////////////////////////////
// AZ::TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override;
//////////////////////////////////////////////////////////////////////////
// SurfaceDataModifierRequestBus
void ModifySurfacePoints(SurfacePointList& surfacePointList) const override;
private:
bool DoRayTrace(const AZ::Vector3& inPosition, bool queryPointOnly, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const;
void UpdateColliderData();
void OnCompositionChanged();
SurfaceDataColliderConfig m_configuration;
SurfaceDataRegistryHandle m_providerHandle = InvalidSurfaceDataRegistryHandle;
SurfaceDataRegistryHandle m_modifierHandle = InvalidSurfaceDataRegistryHandle;
// cached data
AZStd::atomic_bool m_refresh{ false };
mutable AZStd::recursive_mutex m_cacheMutex;
AZ::Aabb m_colliderBounds = AZ::Aabb::CreateNull();
};
}
@@ -0,0 +1,303 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensor's.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "SurfaceDataMeshComponent.h"
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <LmbrCentral/Rendering/MeshAsset.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <SurfaceData/Utility/SurfaceDataUtility.h>
#include <Cry_Matrix34.h>
#include <Cry_GeoIntersect.h>
#include <Cry_Geo.h>
#include <IStatObj.h>
#include <MathConversion.h>
namespace SurfaceData
{
void SurfaceDataMeshConfig::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceDataMeshConfig, AZ::ComponentConfig>()
->Version(0)
->Field("SurfaceTags", &SurfaceDataMeshConfig::m_tags)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<SurfaceDataMeshConfig>(
"Mesh Surface Tag Emitter", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &SurfaceDataMeshConfig::m_tags, "Generated Tags", "")
;
}
}
}
void SurfaceDataMeshComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
}
void SurfaceDataMeshComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
}
void SurfaceDataMeshComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("MeshService", 0x71d8a455));
}
void SurfaceDataMeshComponent::Reflect(AZ::ReflectContext* context)
{
SurfaceDataMeshConfig::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceDataMeshComponent, AZ::Component>()
->Version(0)
->Field("Configuration", &SurfaceDataMeshComponent::m_configuration)
;
}
}
SurfaceDataMeshComponent::SurfaceDataMeshComponent(const SurfaceDataMeshConfig& configuration)
: m_configuration(configuration)
{
}
void SurfaceDataMeshComponent::Activate()
{
AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
LmbrCentral::MeshComponentNotificationBus::Handler::BusConnect(GetEntityId());
m_providerHandle = InvalidSurfaceDataRegistryHandle;
m_refresh = false;
// Update the cached mesh data and bounds, then register the surface data provider
UpdateMeshData();
}
void SurfaceDataMeshComponent::Deactivate()
{
if (m_providerHandle != InvalidSurfaceDataRegistryHandle)
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
}
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
LmbrCentral::MeshComponentNotificationBus::Handler::BusDisconnect();
m_refresh = false;
// Clear the cached mesh data
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
m_meshAssetData = {};
m_meshBounds = AZ::Aabb::CreateNull();
m_meshWorldTM = AZ::Transform::CreateIdentity();
m_meshWorldTMInverse = AZ::Transform::CreateIdentity();
}
}
bool SurfaceDataMeshComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const SurfaceDataMeshConfig*>(baseConfig))
{
m_configuration = *config;
return true;
}
return false;
}
bool SurfaceDataMeshComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<SurfaceDataMeshConfig*>(outBaseConfig))
{
*config = m_configuration;
return true;
}
return false;
}
bool SurfaceDataMeshComponent::DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
// test AABB as first pass to claim the point
const AZ::Vector3 testPosition = AZ::Vector3(
inPosition.GetX(),
inPosition.GetY(),
(m_meshBounds.GetMax().GetZ() + m_meshBounds.GetMin().GetZ()) * 0.5f);
if (!m_meshBounds.Contains(testPosition))
{
return false;
}
LmbrCentral::MeshAsset* mesh = m_meshAssetData.GetAs<LmbrCentral::MeshAsset>();
if (!mesh)
{
return false;
}
const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding);
const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ();
return GetMeshRayIntersection(*mesh, m_meshWorldTM, m_meshWorldTMInverse, rayOrigin, rayDirection, outPosition, outNormal);
}
void SurfaceDataMeshComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{
AZ::Vector3 hitPosition;
AZ::Vector3 hitNormal;
if (DoRayTrace(inPosition, hitPosition, hitNormal))
{
SurfacePoint point;
point.m_entityId = GetEntityId();
point.m_position = hitPosition;
point.m_normal = hitNormal;
AddMaxValueForMasks(point.m_masks, m_configuration.m_tags, 1.0f);
surfacePointList.push_back(point);
}
}
AZ::Aabb SurfaceDataMeshComponent::GetSurfaceAabb() const
{
return m_meshBounds;
}
SurfaceTagVector SurfaceDataMeshComponent::GetSurfaceTags() const
{
return m_configuration.m_tags;
}
void SurfaceDataMeshComponent::OnCompositionChanged()
{
if (!m_refresh)
{
m_refresh = true;
AZ::TickBus::Handler::BusConnect();
}
}
void SurfaceDataMeshComponent::OnMeshDestroyed()
{
OnCompositionChanged();
}
void SurfaceDataMeshComponent::OnMeshCreated(const AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
(void)asset;
OnCompositionChanged();
}
void SurfaceDataMeshComponent::OnBoundsReset()
{
OnCompositionChanged();
}
void SurfaceDataMeshComponent::OnTransformChanged(const AZ::Transform & local, const AZ::Transform & world)
{
(void)local;
(void)world;
OnCompositionChanged();
}
void SurfaceDataMeshComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
if (m_refresh)
{
UpdateMeshData();
m_refresh = false;
}
AZ::TickBus::Handler::BusDisconnect();
}
void SurfaceDataMeshComponent::UpdateMeshData()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
bool meshValidBeforeUpdate = false;
bool meshValidAfterUpdate = false;
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
meshValidBeforeUpdate = (m_meshAssetData.GetAs<LmbrCentral::MeshAsset>() != nullptr) && (m_meshBounds.IsValid());
m_meshAssetData = {};
LmbrCentral::MeshComponentRequestBus::EventResult(m_meshAssetData, GetEntityId(), &LmbrCentral::MeshComponentRequestBus::Events::GetMeshAsset);
m_meshBounds = AZ::Aabb::CreateNull();
LmbrCentral::MeshComponentRequestBus::EventResult(m_meshBounds, GetEntityId(), &LmbrCentral::MeshComponentRequestBus::Events::GetWorldBounds);
m_meshWorldTM = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(m_meshWorldTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
m_meshWorldTMInverse = m_meshWorldTM.GetInverse();
meshValidAfterUpdate = (m_meshAssetData.GetAs<LmbrCentral::MeshAsset>() != nullptr) && (m_meshBounds.IsValid());
}
SurfaceDataRegistryEntry registryEntry;
registryEntry.m_entityId = GetEntityId();
registryEntry.m_bounds = GetSurfaceAabb();
registryEntry.m_tags = GetSurfaceTags();
if (!meshValidBeforeUpdate && !meshValidAfterUpdate)
{
// We didn't have a valid mesh asset before or after running this, so do nothing.
}
else if (!meshValidBeforeUpdate && meshValidAfterUpdate)
{
// Our mesh has become valid, so register as a provider and save off the provider handle
AZ_Assert((m_providerHandle == InvalidSurfaceDataRegistryHandle), "Surface data handle is initialized before our mesh became active");
AZ_Assert(m_meshBounds.IsValid(), "Mesh Geometry isn't correctly initialized.");
SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, registryEntry);
// Start listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle);
}
else if (meshValidBeforeUpdate && !meshValidAfterUpdate)
{
// Our mesh has stopped being valid, so unregister and stop listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
}
else if (meshValidBeforeUpdate && meshValidAfterUpdate)
{
// Our mesh was valid before and after, it just changed in some way, so update our registry entry.
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UpdateSurfaceDataProvider, m_providerHandle, registryEntry);
}
}
}
@@ -0,0 +1,110 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/containers/unordered_map.h>
#include <LmbrCentral/Rendering/MeshComponentBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace LmbrCentral
{
class MeshAsset;
template<typename, typename>
class EditorWrappedComponentBase;
}
namespace SurfaceData
{
constexpr float s_rayAABBHeightPadding = 0.1f;
class SurfaceDataMeshConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(SurfaceDataMeshConfig, AZ::SystemAllocator, 0);
AZ_RTTI(SurfaceDataMeshConfig, "{764C602E-7CA8-4BCC-AB2D-3E46623B3A20}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
SurfaceTagVector m_tags;
};
class SurfaceDataMeshComponent
: public AZ::Component
, public AZ::TickBus::Handler
, public AZ::TransformNotificationBus::Handler
, public LmbrCentral::MeshComponentNotificationBus::Handler
, public SurfaceDataProviderRequestBus::Handler
{
public:
template<typename, typename> friend class LmbrCentral::EditorWrappedComponentBase;
AZ_COMPONENT(SurfaceDataMeshComponent, "{F8915F34-BE8B-40B4-B7E8-01EBF3DA1C95}");
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void Reflect(AZ::ReflectContext* context);
SurfaceDataMeshComponent(const SurfaceDataMeshConfig& configuration);
SurfaceDataMeshComponent() = default;
~SurfaceDataMeshComponent() = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
//////////////////////////////////////////////////////////////////////////
// MeshComponentNotificationBus
void OnMeshCreated(const AZ::Data::Asset<AZ::Data::AssetData>& asset) override;
void OnMeshDestroyed() override;
void OnBoundsReset() override;;
//////////////////////////////////////////////////////////////////////////
// TransformNotificationBus
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
////////////////////////////////////////////////////////////////////////
// AZ::TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override;
private:
bool DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const;
void UpdateMeshData();
void OnCompositionChanged();
AZ::Aabb GetSurfaceAabb() const;
SurfaceTagVector GetSurfaceTags() const;
SurfaceDataMeshConfig m_configuration;
SurfaceDataRegistryHandle m_providerHandle = InvalidSurfaceDataRegistryHandle;
// cached data
AZStd::atomic_bool m_refresh{ false };
mutable AZStd::recursive_mutex m_cacheMutex;
AZ::Data::Asset<AZ::Data::AssetData> m_meshAssetData;
AZ::Transform m_meshWorldTM = AZ::Transform::CreateIdentity();
AZ::Transform m_meshWorldTMInverse = AZ::Transform::CreateIdentity();
AZ::Aabb m_meshBounds = AZ::Aabb::CreateNull();
};
}
@@ -0,0 +1,296 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensor's.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "SurfaceDataShapeComponent.h"
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <SurfaceData/Utility/SurfaceDataUtility.h>
namespace SurfaceData
{
void SurfaceDataShapeConfig::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceDataShapeConfig, AZ::ComponentConfig>()
->Version(0)
->Field("ProviderTags", &SurfaceDataShapeConfig::m_providerTags)
->Field("ModifierTags", &SurfaceDataShapeConfig::m_modifierTags)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<SurfaceDataShapeConfig>(
"Shape Surface Tag Emitter", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &SurfaceDataShapeConfig::m_providerTags, "Generated Tags", "Surface tags to add to created points")
->DataElement(0, &SurfaceDataShapeConfig::m_modifierTags, "Extended Tags", "Surface tags to add to contained points")
;
}
}
}
void SurfaceDataShapeComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
services.push_back(AZ_CRC("SurfaceDataModifierService", 0x68f8aa72));
}
void SurfaceDataShapeComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
services.push_back(AZ_CRC("SurfaceDataModifierService", 0x68f8aa72));
}
void SurfaceDataShapeComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("ShapeService", 0xe86aa5fe));
}
void SurfaceDataShapeComponent::Reflect(AZ::ReflectContext* context)
{
SurfaceDataShapeConfig::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceDataShapeComponent, AZ::Component>()
->Version(0)
->Field("Configuration", &SurfaceDataShapeComponent::m_configuration)
;
}
}
SurfaceDataShapeComponent::SurfaceDataShapeComponent(const SurfaceDataShapeConfig& configuration)
: m_configuration(configuration)
{
}
void SurfaceDataShapeComponent::Activate()
{
m_providerHandle = InvalidSurfaceDataRegistryHandle;
m_modifierHandle = InvalidSurfaceDataRegistryHandle;
m_refresh = false;
AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId());
// Update the cached shape data and bounds, then register the surface data provider / modifier
UpdateShapeData();
}
void SurfaceDataShapeComponent::Deactivate()
{
if (m_providerHandle != InvalidSurfaceDataRegistryHandle)
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
}
if (m_modifierHandle != InvalidSurfaceDataRegistryHandle)
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataModifier, m_modifierHandle);
m_modifierHandle = InvalidSurfaceDataRegistryHandle;
}
m_refresh = false;
AZ::TickBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Handler::BusDisconnect();
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
SurfaceDataModifierRequestBus::Handler::BusDisconnect();
// Clear the cached shape data
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
m_shapeBounds = AZ::Aabb::CreateNull();
m_shapeBoundsIsValid = false;
}
}
bool SurfaceDataShapeComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const SurfaceDataShapeConfig*>(baseConfig))
{
m_configuration = *config;
return true;
}
return false;
}
bool SurfaceDataShapeComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<SurfaceDataShapeConfig*>(outBaseConfig))
{
*config = m_configuration;
return true;
}
return false;
}
void SurfaceDataShapeComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
if (m_shapeBoundsIsValid)
{
const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_shapeBounds.GetMax().GetZ());
const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ();
float intersectionDistance = 0.0f;
bool hitShape = false;
LmbrCentral::ShapeComponentRequestsBus::EventResult(hitShape, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IntersectRay, rayOrigin, rayDirection, intersectionDistance);
if (hitShape)
{
SurfacePoint point;
point.m_entityId = GetEntityId();
point.m_position = rayOrigin + intersectionDistance * rayDirection;
point.m_normal = AZ::Vector3::CreateAxisZ();
AddMaxValueForMasks(point.m_masks, m_configuration.m_providerTags, 1.0f);
surfacePointList.push_back(point);
}
}
}
void SurfaceDataShapeComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
if (m_shapeBoundsIsValid && !m_configuration.m_modifierTags.empty())
{
const AZ::EntityId entityId = GetEntityId();
for (auto& point : surfacePointList)
{
if (point.m_entityId != entityId && m_shapeBounds.Contains(point.m_position))
{
bool inside = false;
LmbrCentral::ShapeComponentRequestsBus::EventResult(inside, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, point.m_position);
if (inside)
{
AddMaxValueForMasks(point.m_masks, m_configuration.m_modifierTags, 1.0f);
}
}
}
}
}
void SurfaceDataShapeComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& /*world*/)
{
OnCompositionChanged();
}
void SurfaceDataShapeComponent::OnShapeChanged([[maybe_unused]] ShapeChangeReasons changeReason)
{
OnCompositionChanged();
}
void SurfaceDataShapeComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
if (m_refresh)
{
UpdateShapeData();
m_refresh = false;
}
AZ::TickBus::Handler::BusDisconnect();
}
void SurfaceDataShapeComponent::OnCompositionChanged()
{
if (!m_refresh)
{
m_refresh = true;
AZ::TickBus::Handler::BusConnect();
}
}
void SurfaceDataShapeComponent::UpdateShapeData()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
bool shapeValidBeforeUpdate = false;
bool shapeValidAfterUpdate = false;
{
AZStd::lock_guard<decltype(m_cacheMutex)> lock(m_cacheMutex);
shapeValidBeforeUpdate = m_shapeBoundsIsValid;
m_shapeBounds = AZ::Aabb::CreateNull();
LmbrCentral::ShapeComponentRequestsBus::EventResult(m_shapeBounds, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
m_shapeBoundsIsValid = m_shapeBounds.IsValid();
shapeValidAfterUpdate = m_shapeBoundsIsValid;
}
SurfaceDataRegistryEntry providerRegistryEntry;
providerRegistryEntry.m_entityId = GetEntityId();
providerRegistryEntry.m_bounds = m_shapeBounds;
providerRegistryEntry.m_tags = m_configuration.m_providerTags;
SurfaceDataRegistryEntry modifierRegistryEntry(providerRegistryEntry);
modifierRegistryEntry.m_tags = m_configuration.m_modifierTags;
if (shapeValidBeforeUpdate && shapeValidAfterUpdate)
{
// Our shape was valid before and after, it just changed in some way, so update our registry entries
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
AZ_Assert((m_modifierHandle != InvalidSurfaceDataRegistryHandle), "Invalid modifier data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UpdateSurfaceDataProvider, m_providerHandle, providerRegistryEntry);
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UpdateSurfaceDataModifier, m_modifierHandle, modifierRegistryEntry);
}
else if (!shapeValidBeforeUpdate && shapeValidAfterUpdate)
{
// Our shape has become valid, so register as a provider and save off the registry handles
AZ_Assert((m_providerHandle == InvalidSurfaceDataRegistryHandle), "Surface Provider data handle is initialized before our shape became valid");
AZ_Assert((m_modifierHandle == InvalidSurfaceDataRegistryHandle), "Surface Modifier data handle is initialized before our shape became valid");
SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, providerRegistryEntry);
SurfaceDataSystemRequestBus::BroadcastResult(m_modifierHandle, &SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataModifier, modifierRegistryEntry);
// Start listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
AZ_Assert((m_modifierHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle);
SurfaceDataModifierRequestBus::Handler::BusConnect(m_modifierHandle);
}
else if (shapeValidBeforeUpdate && !shapeValidAfterUpdate)
{
// Our shape has stopped being valid, so unregister and stop listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
AZ_Assert((m_modifierHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataModifier, m_modifierHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
m_modifierHandle = InvalidSurfaceDataRegistryHandle;
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
SurfaceDataModifierRequestBus::Handler::BusDisconnect();
}
else
{
// We didn't have a valid shape before or after running this, so do nothing.
}
}
const float SurfaceDataShapeComponent::s_rayAABBHeightPadding = 0.1f;
}
@@ -0,0 +1,104 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <LmbrCentral/Shape/ShapeComponentBus.h>
#include <SurfaceData/SurfaceDataModifierRequestBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <SurfaceData/SurfaceDataTypes.h>
namespace LmbrCentral
{
template<typename, typename>
class EditorWrappedComponentBase;
}
namespace SurfaceData
{
class SurfaceDataShapeConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(SurfaceDataShapeConfig, AZ::SystemAllocator, 0);
AZ_RTTI(SurfaceDataShapeConfig, "{1EE196EF-8986-4A2B-B8DD-DA73F85CD597}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
SurfaceTagVector m_providerTags;
SurfaceTagVector m_modifierTags;
};
class SurfaceDataShapeComponent
: public AZ::Component
, private AZ::TickBus::Handler
, private AZ::TransformNotificationBus::Handler
, private LmbrCentral::ShapeComponentNotificationsBus::Handler
, private SurfaceDataModifierRequestBus::Handler
, private SurfaceDataProviderRequestBus::Handler
{
public:
template<typename, typename> friend class LmbrCentral::EditorWrappedComponentBase;
AZ_COMPONENT(SurfaceDataShapeComponent, "{F746C7F6-EF59-45C3-AB5C-011F7AC43415}");
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void Reflect(AZ::ReflectContext* context);
SurfaceDataShapeComponent(const SurfaceDataShapeConfig& configuration);
SurfaceDataShapeComponent() = default;
~SurfaceDataShapeComponent() = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
//////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const;
//////////////////////////////////////////////////////////////////////////
// SurfaceDataModifierRequestBus
void ModifySurfacePoints(SurfacePointList& surfacePointList) const override;
//////////////////////////////////////////////////////////////////////////
// AZ::TransformNotificationBus
void OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& /*world*/) override;
// ShapeComponentNotificationsBus
void OnShapeChanged(ShapeChangeReasons changeReason) override;
////////////////////////////////////////////////////////////////////////
// AZ::TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
private:
void OnCompositionChanged();
void UpdateShapeData();
SurfaceDataShapeConfig m_configuration;
SurfaceDataRegistryHandle m_providerHandle = InvalidSurfaceDataRegistryHandle;
SurfaceDataRegistryHandle m_modifierHandle = InvalidSurfaceDataRegistryHandle;
// cached data
AZStd::atomic_bool m_refresh{ false };
mutable AZStd::recursive_mutex m_cacheMutex;
AZ::Aabb m_shapeBounds = AZ::Aabb::CreateNull();
bool m_shapeBoundsIsValid = false;
static const float s_rayAABBHeightPadding;
};
}
@@ -0,0 +1,26 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "EditorSurfaceDataColliderComponent.h"
#include <AzCore/Serialization/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <LmbrCentral/Dependency/DependencyNotificationBus.h>
namespace SurfaceData
{
void EditorSurfaceDataColliderComponent::Reflect(AZ::ReflectContext* context)
{
BaseClassType::ReflectSubClass<EditorSurfaceDataColliderComponent, BaseClassType>(context, 2, &LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType,2>);
}
}
@@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Module/Module.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
#include <Components/SurfaceDataColliderComponent.h>
#include <LmbrCentral/Component/EditorWrappedComponentBase.h>
namespace SurfaceData
{
class EditorSurfaceDataColliderComponent
: public LmbrCentral::EditorWrappedComponentBase<SurfaceDataColliderComponent, SurfaceDataColliderConfig>
{
public:
using BaseClassType = LmbrCentral::EditorWrappedComponentBase<SurfaceDataColliderComponent, SurfaceDataColliderConfig>;
AZ_EDITOR_COMPONENT(EditorSurfaceDataColliderComponent, "{0F44367D-B9A3-4C25-A769-24993C8BF7A7}", BaseClassType);
static void Reflect(AZ::ReflectContext* context);
static constexpr const char* const s_categoryName = "Surface Data";
static constexpr const char* const s_componentName = "PhysX Collider Surface Tag Emitter";
static constexpr const char* const s_componentDescription = "Enables a physics collider to emit surface tags";
static constexpr const char* const s_icon = "Editor/Icons/Components/SurfaceData.svg";
static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/SurfaceData.png";
static constexpr const char* const s_helpUrl = "https://docs.aws.amazon.com/console/lumberyard/surfacedata/physics-collider-surface-tag-emitter";
};
}
@@ -0,0 +1,26 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "EditorSurfaceDataMeshComponent.h"
#include <AzCore/Serialization/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <LmbrCentral/Dependency/DependencyNotificationBus.h>
namespace SurfaceData
{
void EditorSurfaceDataMeshComponent::Reflect(AZ::ReflectContext* context)
{
BaseClassType::ReflectSubClass<EditorSurfaceDataMeshComponent, BaseClassType>(context, 2, &LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType,2>);
}
}
@@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Module/Module.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
#include <Components/SurfaceDataMeshComponent.h>
#include <LmbrCentral/Component/EditorWrappedComponentBase.h>
namespace SurfaceData
{
class EditorSurfaceDataMeshComponent
: public LmbrCentral::EditorWrappedComponentBase<SurfaceDataMeshComponent, SurfaceDataMeshConfig>
{
public:
using BaseClassType = LmbrCentral::EditorWrappedComponentBase<SurfaceDataMeshComponent, SurfaceDataMeshConfig>;
AZ_EDITOR_COMPONENT(EditorSurfaceDataMeshComponent, "{4D73E979-5463-4B75-AE46-70B1E52CBF43}", BaseClassType);
static void Reflect(AZ::ReflectContext* context);
static constexpr const char* const s_categoryName = "Surface Data";
static constexpr const char* const s_componentName = "Mesh Surface Tag Emitter";
static constexpr const char* const s_componentDescription = "Enables a static mesh to emit surface tags";
static constexpr const char* const s_icon = "Editor/Icons/Components/SurfaceData.svg";
static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/SurfaceData.png";
static constexpr const char* const s_helpUrl = "https://docs.aws.amazon.com/console/lumberyard/surfacedata/mesh-surface-tag-emitter";
};
}
@@ -0,0 +1,27 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "EditorSurfaceDataShapeComponent.h"
#include <AzCore/Serialization/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <LmbrCentral/Dependency/DependencyNotificationBus.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
namespace SurfaceData
{
void EditorSurfaceDataShapeComponent::Reflect(AZ::ReflectContext* context)
{
BaseClassType::ReflectSubClass<EditorSurfaceDataShapeComponent, BaseClassType>(context, 2, &LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType,2>);
}
}
@@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Module/Module.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
#include <Components/SurfaceDataShapeComponent.h>
#include <LmbrCentral/Component/EditorWrappedComponentBase.h>
namespace SurfaceData
{
class EditorSurfaceDataShapeComponent
: public LmbrCentral::EditorWrappedComponentBase<SurfaceDataShapeComponent, SurfaceDataShapeConfig>
{
public:
using BaseClassType = LmbrCentral::EditorWrappedComponentBase<SurfaceDataShapeComponent, SurfaceDataShapeConfig>;
AZ_EDITOR_COMPONENT(EditorSurfaceDataShapeComponent, "{A2E691AC-A027-4B90-A604-689E543BEA91}", BaseClassType);
static void Reflect(AZ::ReflectContext* context);
static constexpr const char* const s_categoryName = "Surface Data";
static constexpr const char* const s_componentName = "Shape Surface Tag Emitter";
static constexpr const char* const s_componentDescription = "Enables a shape to emit surface tags";
static constexpr const char* const s_icon = "Editor/Icons/Components/SurfaceData.svg";
static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/SurfaceData.png";
static constexpr const char* const s_helpUrl = "https://docs.aws.amazon.com/console/lumberyard/surfacedata/shape-surface-tag-emitter";
};
}
@@ -0,0 +1,218 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "EditorSurfaceDataSystemComponent.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Asset/GenericAssetHandler.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
namespace SurfaceData
{
namespace Details
{
AzFramework::GenericAssetHandler<EditorSurfaceTagListAsset>* s_surfaceTagListAssetHandler = nullptr;
void RegisterAssethandlers()
{
s_surfaceTagListAssetHandler = aznew AzFramework::GenericAssetHandler<EditorSurfaceTagListAsset>("Surface Tag Name List", "Other", "surfaceTagNameList");
s_surfaceTagListAssetHandler->Register();
}
void UnregisterAssethandlers()
{
if (s_surfaceTagListAssetHandler)
{
s_surfaceTagListAssetHandler->Unregister();
delete s_surfaceTagListAssetHandler;
s_surfaceTagListAssetHandler = nullptr;
}
}
}
void EditorSurfaceDataSystemConfig::Reflect(AZ::ReflectContext* context)
{
EditorSurfaceTagListAsset::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<EditorSurfaceDataSystemConfig, AZ::ComponentConfig>()
->Version(0)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<EditorSurfaceDataSystemConfig>(
"Editor Surface Data System Config", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
}
void EditorSurfaceDataSystemComponent::Reflect(AZ::ReflectContext* context)
{
EditorSurfaceDataSystemConfig::Reflect(context);
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<EditorSurfaceDataSystemComponent, AzToolsFramework::Components::EditorComponentBase>()
->Version(0)
->Field("Configuration", &EditorSurfaceDataSystemComponent::m_configuration)
;
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<EditorSurfaceDataSystemComponent>("Editor Surface Data System", "Manages discovery and registration of surface tag list assets")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &EditorSurfaceDataSystemComponent::m_configuration, "Configuration", "")
;
}
}
}
void EditorSurfaceDataSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SurfaceDataTagProviderService", 0x21e6b583));
}
void EditorSurfaceDataSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("SurfaceDataTagProviderService", 0x21e6b583));
}
void EditorSurfaceDataSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("SurfaceDataSystemService", 0x1d44d25f));
}
void EditorSurfaceDataSystemComponent::Init()
{
AzToolsFramework::Components::EditorComponentBase::Init();
}
void EditorSurfaceDataSystemComponent::Activate()
{
Details::RegisterAssethandlers();
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
AzToolsFramework::Components::EditorComponentBase::Activate();
SurfaceDataTagProviderRequestBus::Handler::BusConnect();
}
void EditorSurfaceDataSystemComponent::Deactivate()
{
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
AzToolsFramework::Components::EditorComponentBase::Deactivate();
SurfaceDataTagProviderRequestBus::Handler::BusDisconnect();
Details::UnregisterAssethandlers();
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
}
void EditorSurfaceDataSystemComponent::GetRegisteredSurfaceTagNames(SurfaceTagNameSet& masks) const
{
for (const auto& tagName : Constants::s_allTagNames)
{
masks.insert(tagName);
}
for (const auto& assetPair : m_surfaceTagNameAssets)
{
const auto& asset = assetPair.second;
if (asset.IsReady())
{
const auto& tags = asset.Get()->m_surfaceTagNames;
masks.insert(tags.begin(), tags.end());
}
}
}
void EditorSurfaceDataSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/)
{
//automatically register all surface tag list assets
// First run through all the assets and trigger loads on them.
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets,
nullptr,
[this](const AZ::Data::AssetId assetId, const AZ::Data::AssetInfo& assetInfo) {
const auto assetType = azrtti_typeid<EditorSurfaceTagListAsset>();
if (assetInfo.m_assetType == assetType)
{
m_surfaceTagNameAssets[assetId] = AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, AZ::Data::AssetLoadBehavior::Default);
}
},
nullptr);
// After all the loads are triggered, block to make sure they've all completed.
for (auto& asset : m_surfaceTagNameAssets)
{
asset.second.BlockUntilLoadComplete();
}
}
void EditorSurfaceDataSystemComponent::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, assetId);
const auto assetType = azrtti_typeid<EditorSurfaceTagListAsset>();
if (assetInfo.m_assetType == assetType)
{
AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
}
}
void EditorSurfaceDataSystemComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, assetId);
const auto assetType = azrtti_typeid<EditorSurfaceTagListAsset>();
if (assetInfo.m_assetType == assetType)
{
AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
}
}
void EditorSurfaceDataSystemComponent::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& /*assetInfo*/)
{
m_surfaceTagNameAssets.erase(assetId);
}
void EditorSurfaceDataSystemComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
OnAssetReady(asset);
}
void EditorSurfaceDataSystemComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
AddAsset(asset);
}
void EditorSurfaceDataSystemComponent::AddAsset(AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
const auto assetType = azrtti_typeid<EditorSurfaceTagListAsset>();
if (asset.GetType() == assetType)
{
m_surfaceTagNameAssets[asset.GetId()] = asset;
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues);
}
}
}
@@ -0,0 +1,82 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include "EditorSurfaceTagListAsset.h"
#include <AzCore/Component/Component.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <SurfaceData/SurfaceDataTagProviderRequestBus.h>
namespace AZ::Data
{
class AssetInfo;
}
namespace SurfaceData
{
class EditorSurfaceDataSystemConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(EditorSurfaceDataSystemConfig, AZ::SystemAllocator, 0);
AZ_RTTI(EditorSurfaceDataSystemConfig, "{13B511DF-B649-474C-AC32-1E1026DBB303}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
};
class EditorSurfaceDataSystemComponent
: public AzToolsFramework::Components::EditorComponentBase
, private AzFramework::AssetCatalogEventBus::Handler
, private SurfaceDataTagProviderRequestBus::Handler
, private AZ::Data::AssetBus::MultiHandler
{
public:
AZ_EDITOR_COMPONENT(EditorSurfaceDataSystemComponent, "{F3EE5137-856B-4E29-AADD-84F358AEA75F}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
private:
void AddAsset(AZ::Data::Asset<AZ::Data::AssetData>& asset);
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
// SurfaceDataTagProviderRequestBus
void GetRegisteredSurfaceTagNames(SurfaceTagNameSet& names) const override;
////////////////////////////////////////////////////////////////////////
// AzFramework::AssetCatalogEventBus
void OnCatalogLoaded(const char* /*catalogFile*/) override;
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) override;
////////////////////////////////////////////////////////////////////////
// AZ::Data::AssetBus
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
EditorSurfaceDataSystemConfig m_configuration;
AZStd::unordered_map<AZ::Data::AssetId, AZ::Data::Asset<EditorSurfaceTagListAsset>> m_surfaceTagNameAssets;
};
}
@@ -0,0 +1,44 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "EditorSurfaceTagListAsset.h"
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace SurfaceData
{
void EditorSurfaceTagListAsset::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<EditorSurfaceTagListAsset>()
->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true)
->Version(0)
->Field("SurfaceTagNames", &EditorSurfaceTagListAsset::m_surfaceTagNames)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<EditorSurfaceTagListAsset>(
"Surface Tag Name List Asset", "Contains a list of tag names")
->DataElement(0, &EditorSurfaceTagListAsset::m_surfaceTagNames, "Surface Tag Name List", "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, true)
->ElementAttribute(AZ::Edit::Attributes::MaxLength, 64)
;
}
}
}
}
@@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/RTTI/RTTI.h>
namespace AZ
{
class ReflectContext;
}
namespace SurfaceData
{
/**
* Asset containing dictionary of known tags
*/
class EditorSurfaceTagListAsset final
: public AZ::Data::AssetData
{
public:
AZ_RTTI(EditorSurfaceTagListAsset, "{A471B2A9-85FC-4993-842D-1881CBC03A2B}", AZ::Data::AssetData);
AZ_CLASS_ALLOCATOR(EditorSurfaceTagListAsset, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
AZStd::vector<AZStd::string> m_surfaceTagNames;
};
} // namespace SurfaceData
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include <SurfaceDataEditorModule.h>
#include <SurfaceDataSystemComponent.h>
#include <Editor/EditorSurfaceDataSystemComponent.h>
#include <Editor/EditorSurfaceDataColliderComponent.h>
#include <Editor/EditorSurfaceDataMeshComponent.h>
#include <Editor/EditorSurfaceDataShapeComponent.h>
namespace SurfaceData
{
SurfaceDataEditorModule::SurfaceDataEditorModule()
{
m_descriptors.insert(m_descriptors.end(), {
EditorSurfaceDataSystemComponent::CreateDescriptor(),
EditorSurfaceDataColliderComponent::CreateDescriptor(),
EditorSurfaceDataMeshComponent::CreateDescriptor(),
EditorSurfaceDataShapeComponent::CreateDescriptor()
});
}
AZ::ComponentTypeList SurfaceDataEditorModule::GetRequiredSystemComponents() const
{
AZ::ComponentTypeList requiredComponents = SurfaceDataModule::GetRequiredSystemComponents();
requiredComponents.push_back(azrtti_typeid<EditorSurfaceDataSystemComponent>());
return requiredComponents;
}
}
AZ_DECLARE_MODULE_CLASS(Gem_SurfaceDataEditor, SurfaceData::SurfaceDataEditorModule)
@@ -0,0 +1,31 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include "SurfaceData_precompiled.h"
#include <SurfaceDataModule.h>
namespace SurfaceData
{
class SurfaceDataEditorModule
: public SurfaceDataModule
{
public:
AZ_RTTI(SurfaceDataEditorModule, "{B80F2321-B79A-4161-B586-3E508655DFAF}", SurfaceDataModule);
AZ_CLASS_ALLOCATOR(SurfaceDataEditorModule, AZ::SystemAllocator, 0);
SurfaceDataEditorModule();
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
};
}
@@ -0,0 +1,49 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include <SurfaceDataModule.h>
#include <SurfaceDataSystemComponent.h>
#include <Components/SurfaceDataColliderComponent.h>
#include <Components/SurfaceDataMeshComponent.h>
#include <Components/SurfaceDataShapeComponent.h>
#include <TerrainSurfaceDataSystemComponent.h>
namespace SurfaceData
{
SurfaceDataModule::SurfaceDataModule()
{
m_descriptors.insert(m_descriptors.end(), {
SurfaceDataSystemComponent::CreateDescriptor(),
SurfaceDataColliderComponent::CreateDescriptor(),
SurfaceDataMeshComponent::CreateDescriptor(),
SurfaceDataShapeComponent::CreateDescriptor(),
TerrainSurfaceDataSystemComponent::CreateDescriptor(),
});
}
AZ::ComponentTypeList SurfaceDataModule::GetRequiredSystemComponents() const
{
return AZ::ComponentTypeList{
azrtti_typeid<SurfaceDataSystemComponent>(),
azrtti_typeid<TerrainSurfaceDataSystemComponent>(),
};
}
}
#if !defined(SURFACEDATA_EDITOR)
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
// The first parameter should be GemName_GemIdLower
// The second should be the fully qualified name of the class above
AZ_DECLARE_MODULE_CLASS(Gem_SurfaceData, SurfaceData::SurfaceDataModule)
#endif
@@ -0,0 +1,31 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include "SurfaceData_precompiled.h"
#include <AzCore/Module/Module.h>
namespace SurfaceData
{
class SurfaceDataModule
: public AZ::Module
{
public:
AZ_RTTI(SurfaceDataModule, "{B58B7CA8-98C9-4DC8-8607-E094989BBBE2}", AZ::Module);
AZ_CLASS_ALLOCATOR(SurfaceDataModule, AZ::SystemAllocator, 0);
SurfaceDataModule();
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
};
}
@@ -0,0 +1,466 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/sort.h>
#include "SurfaceDataSystemComponent.h"
#include <SurfaceData/SurfaceDataConstants.h>
#include <SurfaceData/SurfaceTag.h>
#include <SurfaceData/SurfaceDataSystemNotificationBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <SurfaceData/SurfaceDataModifierRequestBus.h>
#include <SurfaceData/Utility/SurfaceDataUtility.h>
namespace SurfaceData
{
void SurfaceDataSystemComponent::Reflect(AZ::ReflectContext* context)
{
SurfaceTag::Reflect(context);
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<SurfaceDataSystemComponent, AZ::Component>()
->Version(0)
;
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<SurfaceDataSystemComponent>("Surface Data System", "Manages registration of surface data providers and forwards intersection data requests to them")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<SurfacePoint>()
->Constructor()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Attribute(AZ::Script::Attributes::Module, "surface_data")
->Property("entityId", BehaviorValueProperty(&SurfacePoint::m_entityId))
->Property("position", BehaviorValueProperty(&SurfacePoint::m_position))
->Property("normal", BehaviorValueProperty(&SurfacePoint::m_normal))
->Property("masks", BehaviorValueProperty(&SurfacePoint::m_masks))
;
behaviorContext->Class<SurfaceDataSystemComponent>()
->RequestBus("SurfaceDataSystemRequestBus")
;
behaviorContext->EBus<SurfaceDataSystemRequestBus>("SurfaceDataSystemRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Attribute(AZ::Script::Attributes::Module, "surface_data")
->Event("GetSurfacePoints", &SurfaceDataSystemRequestBus::Events::GetSurfacePoints)
;
behaviorContext->EBus<SurfaceDataSystemNotificationBus>("SurfaceDataSystemNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Attribute(AZ::Script::Attributes::Module, "surface_data")
->Event("OnSurfaceChanged", &SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged)
;
}
}
void SurfaceDataSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SurfaceDataSystemService", 0x1d44d25f));
}
void SurfaceDataSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("SurfaceDataSystemService", 0x1d44d25f));
}
void SurfaceDataSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
(void)required;
}
void SurfaceDataSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
(void)dependent;
}
void SurfaceDataSystemComponent::Init()
{
}
void SurfaceDataSystemComponent::Activate()
{
SurfaceDataSystemRequestBus::Handler::BusConnect();
}
void SurfaceDataSystemComponent::Deactivate()
{
SurfaceDataSystemRequestBus::Handler::BusDisconnect();
}
SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataProvider(const SurfaceDataRegistryEntry& entry)
{
const SurfaceDataRegistryHandle handle = RegisterSurfaceDataProviderInternal(entry);
if (handle != InvalidSurfaceDataRegistryHandle)
{
// Send in the entry's bounds as both the old and new bounds, since a null Aabb for old bounds
// would cause *all* vegetation sectors to get marked as dirty.
SurfaceDataSystemNotificationBus::Broadcast(&SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged, entry.m_entityId, entry.m_bounds, entry.m_bounds);
}
return handle;
}
void SurfaceDataSystemComponent::UnregisterSurfaceDataProvider(const SurfaceDataRegistryHandle& handle)
{
const SurfaceDataRegistryEntry entry = UnregisterSurfaceDataProviderInternal(handle);
if (entry.m_entityId.IsValid())
{
// Send in the entry's bounds as both the old and new bounds, since a null Aabb for new bounds
// would cause *all* vegetation sectors to get marked as dirty.
SurfaceDataSystemNotificationBus::Broadcast(&SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged, entry.m_entityId, entry.m_bounds, entry.m_bounds);
}
}
void SurfaceDataSystemComponent::UpdateSurfaceDataProvider(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry)
{
AZ::Aabb oldBounds = AZ::Aabb::CreateNull();
if (UpdateSurfaceDataProviderInternal(handle, entry, oldBounds))
{
SurfaceDataSystemNotificationBus::Broadcast(&SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged, entry.m_entityId, oldBounds, entry.m_bounds);
}
}
SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataModifier(const SurfaceDataRegistryEntry& entry)
{
const SurfaceDataRegistryHandle handle = RegisterSurfaceDataModifierInternal(entry);
if (handle != InvalidSurfaceDataRegistryHandle)
{
// Send in the entry's bounds as both the old and new bounds, since a null Aabb for old bounds
// would cause *all* vegetation sectors to get marked as dirty.
SurfaceDataSystemNotificationBus::Broadcast(&SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged, entry.m_entityId, entry.m_bounds, entry.m_bounds);
}
return handle;
}
void SurfaceDataSystemComponent::UnregisterSurfaceDataModifier(const SurfaceDataRegistryHandle& handle)
{
const SurfaceDataRegistryEntry entry = UnregisterSurfaceDataModifierInternal(handle);
if (entry.m_entityId.IsValid())
{
// Send in the entry's bounds as both the old and new bounds, since a null Aabb for new bounds
// would cause *all* vegetation sectors to get marked as dirty.
SurfaceDataSystemNotificationBus::Broadcast(&SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged, entry.m_entityId, entry.m_bounds, entry.m_bounds);
}
}
void SurfaceDataSystemComponent::UpdateSurfaceDataModifier(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry)
{
AZ::Aabb oldBounds = AZ::Aabb::CreateNull();
if (UpdateSurfaceDataModifierInternal(handle, entry, oldBounds))
{
SurfaceDataSystemNotificationBus::Broadcast(&SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged, entry.m_entityId, oldBounds, entry.m_bounds);
}
}
void SurfaceDataSystemComponent::RefreshSurfaceData(const AZ::Aabb& dirtyBounds)
{
SurfaceDataSystemNotificationBus::Broadcast(&SurfaceDataSystemNotificationBus::Events::OnSurfaceChanged, AZ::EntityId(), dirtyBounds, dirtyBounds);
}
void SurfaceDataSystemComponent::GetSurfacePoints(const AZ::Vector3& inPosition, const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointList) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
const bool hasDesiredTags = HasValidTags(desiredTags);
const bool hasModifierTags = hasDesiredTags && HasMatchingTags(desiredTags, m_registeredModifierTags);
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
surfacePointList.clear();
//gather all intersecting points
for (const auto& entryPair : m_registeredSurfaceDataProviders)
{
const AZ::u32 entryAddress = entryPair.first;
const SurfaceDataRegistryEntry& entry = entryPair.second;
AZ::Vector3 point2d(inPosition.GetX(), inPosition.GetY(), entry.m_bounds.GetMax().GetZ());
if (!entry.m_bounds.IsValid() || entry.m_bounds.Contains(point2d))
{
if (!hasDesiredTags || hasModifierTags || HasMatchingTags(desiredTags, entry.m_tags))
{
SurfaceDataProviderRequestBus::Event(entryAddress, &SurfaceDataProviderRequestBus::Events::GetSurfacePoints, point2d, surfacePointList);
}
}
}
if (!surfacePointList.empty())
{
//modify or annotate reported points
for (const auto& entryPair : m_registeredSurfaceDataModifiers)
{
const AZ::u32 entryAddress = entryPair.first;
const SurfaceDataRegistryEntry& entry = entryPair.second;
AZ::Vector3 point2d(inPosition.GetX(), inPosition.GetY(), entry.m_bounds.GetMax().GetZ());
if (!entry.m_bounds.IsValid() || entry.m_bounds.Contains(point2d))
{
SurfaceDataModifierRequestBus::Event(entryAddress, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, surfacePointList);
}
}
// After we've finished creating and annotating all the surface points, combine any points together that have effectively the
// same XY coordinates and extremely similar Z values. This produces results that are sorted in decreasing Z order.
// Also, this filters out any remaining points that don't match the desired tag list. This can happen when a surface provider
// doesn't add a desired tag, and a surface modifier has the *potential* to add it, but then doesn't.
CombineSortAndFilterNeighboringPoints(surfacePointList, hasDesiredTags, desiredTags);
}
}
void SurfaceDataSystemComponent::GetSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, const SurfaceTagVector& desiredTags, SurfacePointListPerPosition& surfacePointListPerPosition) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
surfacePointListPerPosition.clear();
surfacePointListPerPosition.reserve(aznumeric_cast<uint32_t>(ceil(inRegion.GetXExtent() / stepSize.GetX())) * aznumeric_cast<uint32_t>(ceil(inRegion.GetYExtent() / stepSize.GetY())));
// Initialize our list-per-position list with every input position to query from the region.
// This is inclusive on the min sides of inRegion, and exclusive on the max sides.
for (float y = inRegion.GetMin().GetY(); y < inRegion.GetMax().GetY(); y += stepSize.GetY())
{
for (float x = inRegion.GetMin().GetX(); x < inRegion.GetMax().GetX(); x += stepSize.GetX())
{
surfacePointListPerPosition.emplace_back(AZ::Vector3(x, y, AZ::Constants::FloatMax), SurfaceData::SurfacePointList{});
}
}
const bool hasDesiredTags = HasValidTags(desiredTags);
const bool hasModifierTags = hasDesiredTags && HasMatchingTags(desiredTags, m_registeredModifierTags);
// Loop through each data provider, and query all the points for each one. This allows us to check the tags and the overall
// AABB bounds just once per provider, instead of once per point. It also allows for an eventual optimization in which we could send
// the list of points directly into each SurfaceDataProvider.
for (const auto& entryPair : m_registeredSurfaceDataProviders)
{
const SurfaceDataRegistryEntry& entry = entryPair.second;
bool alwaysApplies = !entry.m_bounds.IsValid();
if ((!hasDesiredTags || hasModifierTags || HasMatchingTags(desiredTags, entry.m_tags)) &&
( alwaysApplies || AabbOverlaps2D(entry.m_bounds, inRegion) )
)
{
for (auto& surfacePointListAndPoint : surfacePointListPerPosition)
{
const auto& point2d = surfacePointListAndPoint.first;
SurfacePointList& surfacePointList = surfacePointListAndPoint.second;
AZ::Vector3 point3d(point2d.GetX(), point2d.GetY(), entry.m_bounds.GetMax().GetZ());
if (alwaysApplies || entry.m_bounds.Contains(point3d))
{
SurfaceDataProviderRequestBus::Event(entryPair.first, &SurfaceDataProviderRequestBus::Events::GetSurfacePoints, point3d, surfacePointList);
}
}
}
}
// Once we have our list of surface points created, run through the list of surface data modifiers to potentially add
// surface tags / values onto each point. The difference between this and the above loop is that surface data *providers*
// create new surface points, but surface data *modifiers* simply annotate points that have already been created. The modifiers
// are used to annotate points that occur within a volume. A common example is marking points as "underwater" for points that occur
// within a water volume.
for (const auto& entryPair : m_registeredSurfaceDataModifiers)
{
const SurfaceDataRegistryEntry& entry = entryPair.second;
bool alwaysApplies = !entry.m_bounds.IsValid();
if (alwaysApplies || AabbOverlaps2D(entry.m_bounds, inRegion))
{
for (auto& surfacePointListAndPoint : surfacePointListPerPosition)
{
const auto& point2d = surfacePointListAndPoint.first;
SurfacePointList& surfacePointList = surfacePointListAndPoint.second;
if (!surfacePointList.empty())
{
AZ::Vector3 point3d(point2d.GetX(), point2d.GetY(), entry.m_bounds.GetMax().GetZ());
if (alwaysApplies || entry.m_bounds.Contains(point3d))
{
SurfaceDataModifierRequestBus::Event(entryPair.first, &SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, surfacePointList);
}
}
}
}
}
// After we've finished creating and annotating all the surface points, combine any points together that have effectively the
// same XY coordinates and extremely similar Z values. This produces results that are sorted in decreasing Z order.
// Also, this filters out any remaining points that don't match the desired tag list. This can happen when a surface provider
// doesn't add a desired tag, and a surface modifier has the *potential* to add it, but then doesn't.
for (auto& surfacePointListAndPoint : surfacePointListPerPosition)
{
auto& surfacePointList = surfacePointListAndPoint.second;
if (!surfacePointList.empty())
{
CombineSortAndFilterNeighboringPoints(surfacePointList, hasDesiredTags, desiredTags);
}
}
}
void SurfaceDataSystemComponent::CombineSortAndFilterNeighboringPoints(SurfacePointList& sourcePointList, bool hasDesiredTags, const SurfaceTagVector& desiredTags) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
if (sourcePointList.empty())
{
return;
}
// Sorting only makes sense if we have two or more points
if (sourcePointList.size() > 1)
{
//sort by depth/distance before combining points
AZStd::sort(sourcePointList.begin(), sourcePointList.end(), [](const SurfacePoint& a, const SurfacePoint& b)
{
return a.m_position.GetZ() > b.m_position.GetZ();
});
}
//efficient point consolidation requires the points to be pre-sorted so we are only comparing/combining neighbors
const size_t sourcePointCount = sourcePointList.size();
size_t targetPointIndex = 0;
size_t sourcePointIndex = 0;
m_targetPointList.clear();
m_targetPointList.reserve(sourcePointCount);
// Locate the first point that matches our desired tags, if one exists.
for (sourcePointIndex = 0; sourcePointIndex < sourcePointCount; sourcePointIndex++)
{
if (!hasDesiredTags || (HasMatchingTags(sourcePointList[sourcePointIndex].m_masks, desiredTags)))
{
break;
}
}
if (sourcePointIndex < sourcePointCount)
{
// We found a point that matches our tags, so add it to our target list as the first point.
m_targetPointList.push_back(sourcePointList[sourcePointIndex++]);
//iterate over subsequent source points for comparison and consolidation with the last added target/unique point
for (; sourcePointIndex < sourcePointCount; ++sourcePointIndex)
{
const auto& sourcePoint = sourcePointList[sourcePointIndex];
if (!hasDesiredTags || (HasMatchingTags(sourcePoint.m_masks, desiredTags)))
{
auto& targetPoint = m_targetPointList[targetPointIndex];
// [LY-90907] need to add a configurable tolerance for comparison
if (targetPoint.m_position.IsClose(sourcePoint.m_position) &&
targetPoint.m_normal.IsClose(sourcePoint.m_normal))
{
//consolidate points with similar attributes by adding masks to the target point and ignoring the source
AddMaxValueForMasks(targetPoint.m_masks, sourcePoint.m_masks);
continue;
}
//if the points were too different, we have to add a new target point to compare against
m_targetPointList.push_back(sourcePoint);
++targetPointIndex;
}
}
AZStd::swap(sourcePointList, m_targetPointList);
}
}
SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataProviderInternal(const SurfaceDataRegistryEntry& entry)
{
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
SurfaceDataRegistryHandle handle = ++m_registeredSurfaceDataProviderHandleCounter;
m_registeredSurfaceDataProviders[handle] = entry;
return handle;
}
SurfaceDataRegistryEntry SurfaceDataSystemComponent::UnregisterSurfaceDataProviderInternal(const SurfaceDataRegistryHandle& handle)
{
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
SurfaceDataRegistryEntry entry;
auto entryItr = m_registeredSurfaceDataProviders.find(handle);
if (entryItr != m_registeredSurfaceDataProviders.end())
{
entry = entryItr->second;
m_registeredSurfaceDataProviders.erase(entryItr);
}
return entry;
}
bool SurfaceDataSystemComponent::UpdateSurfaceDataProviderInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds)
{
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
auto entryItr = m_registeredSurfaceDataProviders.find(handle);
if (entryItr != m_registeredSurfaceDataProviders.end())
{
oldBounds = entryItr->second.m_bounds;
entryItr->second = entry;
return true;
}
return false;
}
SurfaceDataRegistryHandle SurfaceDataSystemComponent::RegisterSurfaceDataModifierInternal(const SurfaceDataRegistryEntry& entry)
{
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
SurfaceDataRegistryHandle handle = ++m_registeredSurfaceDataModifierHandleCounter;
m_registeredSurfaceDataModifiers[handle] = entry;
m_registeredModifierTags.insert(entry.m_tags.begin(), entry.m_tags.end());
return handle;
}
SurfaceDataRegistryEntry SurfaceDataSystemComponent::UnregisterSurfaceDataModifierInternal(const SurfaceDataRegistryHandle& handle)
{
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
SurfaceDataRegistryEntry entry;
auto entryItr = m_registeredSurfaceDataModifiers.find(handle);
if (entryItr != m_registeredSurfaceDataModifiers.end())
{
entry = entryItr->second;
m_registeredSurfaceDataModifiers.erase(entryItr);
}
return entry;
}
bool SurfaceDataSystemComponent::UpdateSurfaceDataModifierInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds)
{
AZStd::lock_guard<decltype(m_registrationMutex)> registrationLock(m_registrationMutex);
auto entryItr = m_registeredSurfaceDataModifiers.find(handle);
if (entryItr != m_registeredSurfaceDataModifiers.end())
{
oldBounds = entryItr->second.m_bounds;
entryItr->second = entry;
m_registeredModifierTags.insert(entry.m_tags.begin(), entry.m_tags.end());
return true;
}
return false;
}
}
@@ -0,0 +1,78 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Math/Aabb.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
namespace SurfaceData
{
class SurfaceDataSystemComponent
: public AZ::Component
, private SurfaceDataSystemRequestBus::Handler
{
public:
AZ_COMPONENT(SurfaceDataSystemComponent, "{6F334BAA-7BD5-45F8-A9BA-760667D25FA0}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
// SurfaceDataSystemRequestBus implementation
void GetSurfacePoints(const AZ::Vector3& inPosition, const SurfaceTagVector& desiredTags, SurfacePointList& surfacePointList) const override;
void GetSurfacePointsFromRegion(const AZ::Aabb& inRegion, const AZ::Vector2 stepSize, const SurfaceTagVector& desiredTags, SurfacePointListPerPosition& surfacePointListPerPosition) const override;
SurfaceDataRegistryHandle RegisterSurfaceDataProvider(const SurfaceDataRegistryEntry& entry) override;
void UnregisterSurfaceDataProvider(const SurfaceDataRegistryHandle& handle) override;
void UpdateSurfaceDataProvider(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry) override;
SurfaceDataRegistryHandle RegisterSurfaceDataModifier(const SurfaceDataRegistryEntry& entry) override;
void UnregisterSurfaceDataModifier(const SurfaceDataRegistryHandle& handle) override;
void UpdateSurfaceDataModifier(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry) override;
void RefreshSurfaceData(const AZ::Aabb& dirtyArea) override;
private:
void CombineSortAndFilterNeighboringPoints(SurfacePointList& sourcePointList, bool hasDesiredTags, const SurfaceTagVector& desiredTags) const;
SurfaceDataRegistryHandle RegisterSurfaceDataProviderInternal(const SurfaceDataRegistryEntry& entry);
SurfaceDataRegistryEntry UnregisterSurfaceDataProviderInternal(const SurfaceDataRegistryHandle& handle);
bool UpdateSurfaceDataProviderInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds);
SurfaceDataRegistryHandle RegisterSurfaceDataModifierInternal(const SurfaceDataRegistryEntry& entry);
SurfaceDataRegistryEntry UnregisterSurfaceDataModifierInternal(const SurfaceDataRegistryHandle& handle);
bool UpdateSurfaceDataModifierInternal(const SurfaceDataRegistryHandle& handle, const SurfaceDataRegistryEntry& entry, AZ::Aabb& oldBounds);
mutable AZStd::recursive_mutex m_registrationMutex;
AZStd::unordered_map<SurfaceDataRegistryHandle, SurfaceDataRegistryEntry> m_registeredSurfaceDataProviders;
AZStd::unordered_map<SurfaceDataRegistryHandle, SurfaceDataRegistryEntry> m_registeredSurfaceDataModifiers;
SurfaceDataRegistryHandle m_registeredSurfaceDataProviderHandleCounter = InvalidSurfaceDataRegistryHandle;
SurfaceDataRegistryHandle m_registeredSurfaceDataModifierHandleCounter = InvalidSurfaceDataRegistryHandle;
AZStd::unordered_set<AZ::u32> m_registeredModifierTags;
//point vector reserved for reuse
mutable SurfacePointList m_targetPointList;
};
}
@@ -0,0 +1,12 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
@@ -0,0 +1,14 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <platform.h> // Many CryCommon files require that this is included first.
+167
View File
@@ -0,0 +1,167 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include <SurfaceData/SurfaceTag.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <SurfaceData/SurfaceDataTagProviderRequestBus.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/sort.h>
namespace SurfaceData
{
namespace SurfaceTagUtil
{
static bool UpdateVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() < 1)
{
AZStd::string surfaceTag;
if (classElement.GetChildData(AZ_CRC("SurfaceTag", 0xafd98787), surfaceTag))
{
classElement.RemoveElementByName(AZ_CRC("SurfaceTag", 0xafd98787));
classElement.AddElementWithData(context, "SurfaceTagCrc", (AZ::u32)(AZ::Crc32(surfaceTag.data())));
}
}
if (classElement.GetVersion() < 2)
{
SurfaceTag surfaceTag;
if (classElement.GetData(surfaceTag))
{
if (surfaceTag == AZ_CRC("(default)", 0x3c3d0dd8))
{
surfaceTag = Constants::s_unassignedTagCrc;
classElement.SetData(context, surfaceTag);
}
}
}
return true;
}
}
void SurfaceTag::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SurfaceTag>()
->Version(2, &SurfaceTagUtil::UpdateVersion)
->Field("SurfaceTagCrc", &SurfaceTag::m_surfaceTagCrc)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<SurfaceTag>(
"Surface Tag", "Matches a surface value like a mask or material")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &SurfaceTag::m_surfaceTagCrc, "Surface Tag", "Matches a surface value like a mask or material")
->Attribute(AZ::Edit::Attributes::EnumValues, &SurfaceTag::BuildSelectableTagList)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<SurfaceTag>()
->Constructor()
->Constructor<const AZStd::string&>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Category, "Vegetation")
->Attribute(AZ::Script::Attributes::Module, "surface_data")
->Method("SetTag", &SurfaceTag::SetTag)
->Method("Equal", &SurfaceTag::operator==)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
;
}
}
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> SurfaceTag::GetRegisteredTags()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
SurfaceTagNameSet labels;
SurfaceDataTagProviderRequestBus::Broadcast(&SurfaceDataTagProviderRequestBus::Events::GetRegisteredSurfaceTagNames, labels);
labels.insert(Constants::s_unassignedTagName);
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> registeredTags;
registeredTags.reserve(labels.size());
for (const auto& label : labels)
{
const AZ::u32 crc = AZ::Crc32(label.data());
//warn when two tags have the same crc
auto entryItr = AZStd::find_if(registeredTags.begin(), registeredTags.end(), [crc](const auto& entry) {return entry.first == crc;});
if (entryItr != registeredTags.end())
{
AZ_Warning("SurfaceData", false, "SurfaceTag CRC collision between \"%s\" and \"%s\"! \"%s\" not added.", entryItr->second.data(), label.data(), label.data());
continue;
}
registeredTags.push_back({ crc, label });
}
return registeredTags;
}
bool SurfaceTag::FindDisplayName(const AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>>& selectableTags, AZStd::string& name) const
{
auto it = AZStd::find_if(
selectableTags.begin(),
selectableTags.end(),
[this](const auto& entry) {return m_surfaceTagCrc == entry.first; });
if (it == selectableTags.end())
{
//if a match was not found, generate a name using the crc
name = AZStd::string::format("(unregistered %u)", m_surfaceTagCrc);
return false;
}
name = it->second;
return true;
}
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> SurfaceTag::BuildSelectableTagList() const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> selectableTags = GetRegisteredTags();
AZStd::string name;
if (!FindDisplayName(selectableTags, name))
{
//if a match was not found, add the generated name to the selectable set
selectableTags.push_back({ m_surfaceTagCrc, name });
AZ_Warning("SurfaceData", false, "SurfaceTag CRC %u is not a registered tag.", m_surfaceTagCrc);
}
AZStd::sort(selectableTags.begin(), selectableTags.end(), [](const auto& lhs, const auto& rhs) {return lhs.second < rhs.second;});
return selectableTags;
}
AZStd::string SurfaceTag::GetDisplayName() const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity);
AZStd::string name;
FindDisplayName(GetRegisteredTags(), name);
return name;
}
}
@@ -0,0 +1,267 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include "TerrainSurfaceDataSystemComponent.h"
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <MathConversion.h>
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
#include <SurfaceData/SurfaceTag.h>
#include <SurfaceData/Utility/SurfaceDataUtility.h>
#include <ISystem.h>
#include <I3DEngine.h>
namespace SurfaceData
{
//////////////////////////////////////////////////////////////////////////
// TerrainSurfaceDataSystemConfig
void TerrainSurfaceDataSystemConfig::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<TerrainSurfaceDataSystemConfig, AZ::ComponentConfig>()
->Version(0)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<TerrainSurfaceDataSystemConfig>(
"Terrain Surface Data System", "Configures management of surface data requests against legacy terrain")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
}
//////////////////////////////////////////////////////////////////////////
// TerrainSurfaceDataSystemComponent
void TerrainSurfaceDataSystemComponent::Reflect(AZ::ReflectContext* context)
{
TerrainSurfaceDataSystemConfig::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<TerrainSurfaceDataSystemComponent, AZ::Component>()
->Version(0)
->Field("Configuration", &TerrainSurfaceDataSystemComponent::m_configuration)
;
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class<TerrainSurfaceDataSystemComponent>("Terrain Surface Data System", "Manages surface data requests against legacy terrain")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Surface Data")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &TerrainSurfaceDataSystemComponent::m_configuration, "Configuration", "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}
}
TerrainSurfaceDataSystemComponent::TerrainSurfaceDataSystemComponent(const TerrainSurfaceDataSystemConfig& configuration)
: m_configuration(configuration)
{
}
TerrainSurfaceDataSystemComponent::TerrainSurfaceDataSystemComponent()
{
}
void TerrainSurfaceDataSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataProviderService", 0xfe9fb95e));
services.push_back(AZ_CRC("TerrainSurfaceDataProviderService", 0xa1ac7717));
}
void TerrainSurfaceDataSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("TerrainSurfaceDataProviderService", 0xa1ac7717));
}
void TerrainSurfaceDataSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
services.push_back(AZ_CRC("SurfaceDataSystemService", 0x1d44d25f));
}
void TerrainSurfaceDataSystemComponent::Activate()
{
m_providerHandle = InvalidSurfaceDataRegistryHandle;
m_system = GetISystem();
CrySystemEventBus::Handler::BusConnect();
AZ::HeightmapUpdateNotificationBus::Handler::BusConnect();
UpdateTerrainData(AZ::Aabb::CreateNull());
}
void TerrainSurfaceDataSystemComponent::Deactivate()
{
if (m_providerHandle != InvalidSurfaceDataRegistryHandle)
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
}
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
AZ::HeightmapUpdateNotificationBus::Handler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
m_system = nullptr;
// Clear the cached terrain bounds data
{
m_terrainBounds = AZ::Aabb::CreateNull();
m_terrainBoundsIsValid = false;
}
}
bool TerrainSurfaceDataSystemComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (const auto config = azrtti_cast<const TerrainSurfaceDataSystemConfig*>(baseConfig))
{
m_configuration = *config;
return true;
}
return false;
}
bool TerrainSurfaceDataSystemComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<TerrainSurfaceDataSystemConfig*>(outBaseConfig))
{
*config = m_configuration;
return true;
}
return false;
}
void TerrainSurfaceDataSystemComponent::OnCrySystemInitialized(ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams)
{
m_system = &system;
}
void TerrainSurfaceDataSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system)
{
m_system = nullptr;
}
void TerrainSurfaceDataSystemComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{
if (m_terrainBoundsIsValid)
{
auto enumerationCallback = [&](AzFramework::Terrain::TerrainDataRequests* terrain) -> bool
{
if (terrain->GetTerrainAabb().Contains(inPosition))
{
bool isTerrainValidAtPoint = false;
const float terrainHeight = terrain->GetHeight(inPosition, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR, &isTerrainValidAtPoint);
const bool isHole = !isTerrainValidAtPoint;
SurfacePoint point;
point.m_entityId = GetEntityId();
point.m_position = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), terrainHeight);
point.m_normal = terrain->GetNormal(inPosition);
const AZ::Crc32 terrainTag = isHole ? Constants::s_terrainHoleTagCrc : Constants::s_terrainTagCrc;
AddMaxValueForMasks(point.m_masks, terrainTag, 1.0f);
surfacePointList.push_back(point);
}
// Only one handler should exist.
return false;
};
AzFramework::Terrain::TerrainDataRequestBus::EnumerateHandlers(enumerationCallback);
}
}
AZ::Aabb TerrainSurfaceDataSystemComponent::GetSurfaceAabb() const
{
auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler();
return terrain ? terrain->GetTerrainAabb() : AZ::Aabb::CreateNull();
}
SurfaceTagVector TerrainSurfaceDataSystemComponent::GetSurfaceTags() const
{
SurfaceTagVector tags;
tags.push_back(Constants::s_terrainHoleTagCrc);
tags.push_back(Constants::s_terrainTagCrc);
return tags;
}
void TerrainSurfaceDataSystemComponent::UpdateTerrainData(const AZ::Aabb& dirtyRegion)
{
bool terrainValidBeforeUpdate = m_terrainBoundsIsValid;
bool terrainValidAfterUpdate = false;
AZ::Aabb terrainBoundsBeforeUpdate = m_terrainBounds;
SurfaceDataRegistryEntry registryEntry;
registryEntry.m_entityId = GetEntityId();
registryEntry.m_bounds = GetSurfaceAabb();
registryEntry.m_tags = GetSurfaceTags();
m_terrainBounds = registryEntry.m_bounds;
m_terrainBoundsIsValid = m_terrainBounds.IsValid();
terrainValidAfterUpdate = m_terrainBoundsIsValid;
if (terrainValidBeforeUpdate && terrainValidAfterUpdate)
{
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
// Our terrain was valid before and after, it just changed in some way. If we have a valid dirty region passed in
// then it's possible that the heightmap has been modified in the Editor. Otherwise, just notify that the entire
// terrain has changed in some way.
if (dirtyRegion.IsValid())
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::RefreshSurfaceData, dirtyRegion);
}
else
{
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UpdateSurfaceDataProvider, m_providerHandle, registryEntry);
}
}
else if (!terrainValidBeforeUpdate && terrainValidAfterUpdate)
{
// Our terrain has become valid, so register as a provider and save off the registry handles
AZ_Assert((m_providerHandle == InvalidSurfaceDataRegistryHandle), "Surface Provider data handle is initialized before our terrain became valid");
SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, registryEntry);
// Start listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle);
}
else if (terrainValidBeforeUpdate && !terrainValidAfterUpdate)
{
// Our terrain has stopped being valid, so unregister and stop listening for surface data events
AZ_Assert((m_providerHandle != InvalidSurfaceDataRegistryHandle), "Invalid surface data handle");
SurfaceDataSystemRequestBus::Broadcast(&SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
m_providerHandle = InvalidSurfaceDataRegistryHandle;
SurfaceDataProviderRequestBus::Handler::BusDisconnect();
}
else
{
// We didn't have a valid terrain before or after running this, so do nothing.
}
}
void TerrainSurfaceDataSystemComponent::HeightmapModified(const AZ::Aabb& bounds)
{
UpdateTerrainData(bounds);
}
}
@@ -0,0 +1,88 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/EBus/EBus.h>
#include <CrySystemBus.h>
#include <HeightmapUpdateNotificationBus.h>
#include <SurfaceData/SurfaceDataModifierRequestBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
namespace SurfaceData
{
class TerrainSurfaceDataSystemConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(TerrainSurfaceDataSystemConfig, AZ::SystemAllocator, 0);
AZ_RTTI(TerrainSurfaceDataSystemConfig, "{2B93F5E5-5346-47A1-9C4D-EFBC6BDF468F}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
};
/**
* The system component to serve for the game side queries for surface values
*/
class TerrainSurfaceDataSystemComponent
: public AZ::Component
, private SurfaceDataProviderRequestBus::Handler
, private AZ::HeightmapUpdateNotificationBus::Handler
, private CrySystemEventBus::Handler
{
friend class EditorTerrainSurfaceDataSystemComponent;
TerrainSurfaceDataSystemComponent(const TerrainSurfaceDataSystemConfig&);
public:
TerrainSurfaceDataSystemComponent();
//////////////////////////////////////////////////////////////////////////
// Component static
AZ_COMPONENT(TerrainSurfaceDataSystemComponent, "{0C821DA4-6DB1-4860-BE25-CB57B3E3F4D4}", AZ::Component);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void Reflect(AZ::ReflectContext* context);
//////////////////////////////////////////////////////////////////////////
// Component
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
//////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const;
////////////////////////////////////////////////////////////////////////////
// CrySystemEvents
void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override;
void OnCrySystemShutdown(ISystem& system) override;
//////////////////////////////////////////////////////////////////////////
// AZ::HeightmapUpdateNotificationBus
void HeightmapModified(const AZ::Aabb& bounds) override;
private:
void UpdateTerrainData(const AZ::Aabb& dirtyRegion);
AZ::Aabb GetSurfaceAabb() const;
SurfaceTagVector GetSurfaceTags() const;
SurfaceDataRegistryHandle m_providerHandle = InvalidSurfaceDataRegistryHandle;
TerrainSurfaceDataSystemConfig m_configuration;
ISystem* m_system = nullptr;
AZ::Aabb m_terrainBounds = AZ::Aabb::CreateNull();
AZStd::atomic_bool m_terrainBoundsIsValid{ false };
};
}
@@ -0,0 +1,360 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include <SurfaceData/Tests/SurfaceDataTestMocks.h>
#include <AzTest/AzTest.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/TransformBus.h>
#include <Source/Components/SurfaceDataColliderComponent.h>
#include <AzFramework/Physics/Casts.h>
#include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/WorldBodyBus.h>
namespace UnitTest
{
// Mock out a generic Physics Collider Component, which is a required dependency for adding a SurfaceDataColliderComponent.
struct MockPhysicsColliderComponent
: public AZ::Component
{
public:
AZ_COMPONENT(MockPhysicsColliderComponent, "{4F7C36DE-6475-4E0A-96A7-BFAF21C07C95}", AZ::Component);
void Activate() override {}
void Deactivate() override {}
static void Reflect(AZ::ReflectContext* reflect) { AZ_UNUSED(reflect); }
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("PhysXColliderService", 0x4ff43f7c));
}
};
class MockPhysicsWorldBusProvider
: public Physics::WorldBodyRequestBus::Handler
{
public:
MockPhysicsWorldBusProvider(const AZ::EntityId& id, AZ::Vector3 inPosition, bool setHitResult, const SurfaceData::SurfacePoint& hitResult)
{
Physics::WorldBodyRequestBus::Handler::BusConnect(id);
// Whether or not the test should return a successful hit, we still want to create a valid
// AABB so that the SurfaceData component registers itself as a provider.
m_aabb = AZ::Aabb::CreateCenterRadius(inPosition, 1.0f);
// Only initialize our mock physics to return a raycast result if the test wants the point to hit.
if (setHitResult)
{
m_rayCastHit.m_distance = 0.0f;
m_rayCastHit.m_position = hitResult.m_position;
m_rayCastHit.m_normal = hitResult.m_normal;
// Just need to set this to a non-null value, it gets checked vs null but not otherwise used.
m_rayCastHit.m_body = reinterpret_cast<Physics::WorldBody*>(1);
}
}
virtual ~MockPhysicsWorldBusProvider()
{
Physics::WorldBodyRequestBus::Handler::BusDisconnect();
}
// Minimal mocks needed to mock out this ebus
void EnablePhysics() override {}
void DisablePhysics() override {}
bool IsPhysicsEnabled() const override { return true; }
Physics::WorldBody* GetWorldBody() override { return nullptr; }
// Functional mocks to mock out the data needed by the component
AZ::Aabb GetAabb() const override { return m_aabb; }
Physics::RayCastHit RayCast([[maybe_unused]] const Physics::RayCastRequest& request) override { return m_rayCastHit; }
AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
Physics::RayCastHit m_rayCastHit;
};
// Provide a set of common helper methods for our tests.
struct SurfaceDataTestFixture
: public SurfaceDataTest
{
protected:
// Create a new SurfacePoint with the given fields.
SurfaceData::SurfacePoint CreateSurfacePoint(AZ::EntityId id, AZ::Vector3 position, AZ::Vector3 normal, AZStd::vector<AZStd::pair<AZStd::string, float>> tags)
{
SurfaceData::SurfacePoint point;
point.m_entityId = id;
point.m_position = position;
point.m_normal = normal;
for (auto& tag : tags)
{
point.m_masks[SurfaceData::SurfaceTag(tag.first)] = tag.second;
}
return point;
}
// Compare two surface points.
bool SurfacePointsAreEqual(const SurfaceData::SurfacePoint& lhs, const SurfaceData::SurfacePoint& rhs)
{
return (lhs.m_entityId == rhs.m_entityId)
&& (lhs.m_position == rhs.m_position)
&& (lhs.m_normal == rhs.m_normal)
&& (lhs.m_masks == rhs.m_masks);
}
// Common test function for testing the "Provider" functionality of the component.
// Given a set of tags and an expected output, check to see if the component provides the
// expected output point.
void TestSurfaceDataColliderProvider(AZStd::vector<AZStd::string> providerTags, bool pointOnProvider,
AZ::Vector3 queryPoint, const SurfaceData::SurfacePoint& expectedOutput)
{
// This lets our component register with surfaceData successfully.
MockSurfaceDataSystem mockSurfaceDataSystem;
// Create the test configuration for the SurfaceDataColliderComponent component
SurfaceData::SurfaceDataColliderConfig config;
for (auto& tag : providerTags)
{
config.m_providerTags.emplace_back(tag);
}
// Create the test entity with the SurfaceDataCollider component and the required physics collider dependency
auto entity = CreateEntity();
// Initialize our Entity ID to the one passed in on the expectedOutput
entity->SetId(expectedOutput.m_entityId);
// Create the components
CreateComponent<MockPhysicsColliderComponent>(entity.get());
CreateComponent<SurfaceData::SurfaceDataColliderComponent>(entity.get(), config);
// Before activating the entity, set up our mock physics provider for this entity
MockPhysicsWorldBusProvider mockPhysics(entity->GetId(), expectedOutput.m_position, pointOnProvider, expectedOutput);
// Now that our mocks are set up, activate the entity.
ActivateEntity(entity.get());
// Get our registered provider handle (and verify that it's valid)
auto providerHandle = mockSurfaceDataSystem.GetSurfaceProviderHandle(entity->GetId());
EXPECT_TRUE(providerHandle != SurfaceData::InvalidSurfaceDataRegistryHandle);
// Call GetSurfacePoints and verify the results
SurfaceData::SurfacePointList pointList;
SurfaceData::SurfaceDataProviderRequestBus::Event(providerHandle, &SurfaceData::SurfaceDataProviderRequestBus::Events::GetSurfacePoints,
queryPoint, pointList);
if (pointOnProvider)
{
ASSERT_TRUE(pointList.size() == 1);
EXPECT_TRUE(SurfacePointsAreEqual(pointList[0], expectedOutput));
}
else
{
EXPECT_TRUE(pointList.empty());
}
}
void TestSurfaceDataColliderModifier(AZStd::vector<AZStd::string> modifierTags,
const SurfaceData::SurfacePoint& input, bool pointInCollider, const SurfaceData::SurfacePoint& expectedOutput)
{
// This lets our component register with surfaceData successfully.
MockSurfaceDataSystem mockSurfaceDataSystem;
// Create the test configuration for the SurfaceDataColliderComponent component
SurfaceData::SurfaceDataColliderConfig config;
for (auto& tag : modifierTags)
{
config.m_modifierTags.emplace_back(tag);
}
// Create the test entity with the SurfaceDataCollider component and the required physics collider dependency
auto entity = CreateEntity();
CreateComponent<MockPhysicsColliderComponent>(entity.get());
CreateComponent<SurfaceData::SurfaceDataColliderComponent>(entity.get(), config);
// Before activating the entity, set up our mock physics provider for this entity
MockPhysicsWorldBusProvider mockPhysics(entity->GetId(), input.m_position, pointInCollider, expectedOutput);
// Now that our mocks are set up, activate the entity.
ActivateEntity(entity.get());
// Get our registered modifier handle (and verify that it's valid)
auto modifierHandle = mockSurfaceDataSystem.GetSurfaceModifierHandle(entity->GetId());
EXPECT_TRUE(modifierHandle != SurfaceData::InvalidSurfaceDataRegistryHandle);
// Call ModifySurfacePoints and verify the results
SurfaceData::SurfacePointList pointList;
pointList.emplace_back(input);
SurfaceData::SurfaceDataModifierRequestBus::Event(modifierHandle, &SurfaceData::SurfaceDataModifierRequestBus::Events::ModifySurfacePoints, pointList);
ASSERT_TRUE(pointList.size() == 1);
EXPECT_TRUE(SurfacePointsAreEqual(pointList[0], expectedOutput));
}
};
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_CreateComponent)
{
// Verify that we can trivially create and destroy the component.
// This lets our component potentially register with surfaceData successfully.
MockSurfaceDataSystem mockSurfaceDataSystem;
// Create an empty configuration for the SurfaceDataColliderComponent component
SurfaceData::SurfaceDataColliderConfig config;
// Create the test entity with the SurfaceDataCollider component with the required PhysicsCollider dependency
auto entity = CreateEntity();
CreateComponent<MockPhysicsColliderComponent>(entity.get());
CreateComponent<SurfaceData::SurfaceDataColliderComponent>(entity.get(), config);
ActivateEntity(entity.get());
// Verify that we haven't registered as a provider or modifier, because we never mocked up a valid AABB
// for this collider.
auto providerHandle = mockSurfaceDataSystem.GetSurfaceProviderHandle(entity->GetId());
auto modifierHandle = mockSurfaceDataSystem.GetSurfaceModifierHandle(entity->GetId());
EXPECT_TRUE(providerHandle == SurfaceData::InvalidSurfaceDataRegistryHandle);
EXPECT_TRUE(modifierHandle == SurfaceData::InvalidSurfaceDataRegistryHandle);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_ProvidePointOnCollider)
{
// Verify that for a point on the collider, the output point contains the correct tag and value.
// Set the expected output to an arbitrary entity ID, position, and normal.
// We'll use this to initialize the mock physics, so the output of the query should match.
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
// Query from the same XY, but one unit higher on Z, just so we can verify that the output returns the collision
// result, not the input point.
constexpr bool pointOnCollider = true;
TestSurfaceDataColliderProvider({ tag }, pointOnCollider, expectedOutput.m_position + AZ::Vector3::CreateAxisZ(), expectedOutput);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_DoNotProvidePointNotOnCollider)
{
// Verify that for a point not on the collider, the output point is empty.
// Set the expected output to an arbitrary entity ID, position, and normal.
// We'll use this to initialize the mock physics.
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
// Query from the same XY, but one unit higher on Z. However, we're also telling our test to provide
// a "no hit" result from physics, so the expectedOutput will be ignored on the result check, and instead
// the output will be verified to be an empty list of points.
constexpr bool pointOnCollider = true;
TestSurfaceDataColliderProvider({ tag }, !pointOnCollider, expectedOutput.m_position + AZ::Vector3::CreateAxisZ(), expectedOutput);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_ProvidePointOnColliderWithMultipleTags)
{
// Verify that if the component has multiple tags, all of them get put on the output with the same value.
// Set the expected output to an arbitrary entity ID, position, and normal.
// We'll use this to initialize the mock physics.
const char* tag1 = "test_mask1";
const char* tag2 = "test_mask2";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3::CreateAxisZ(),
{ AZStd::make_pair<AZStd::string, float>(tag1, 1.0f),
AZStd::make_pair<AZStd::string, float>(tag2, 1.0f) });
// Query from the same XY, but one unit higher on Z, just so we can verify that the output returns the collision
// result, not the input point.
constexpr bool pointOnCollider = true;
TestSurfaceDataColliderProvider({ tag1, tag2 }, pointOnCollider, expectedOutput.m_position + AZ::Vector3::CreateAxisZ(), expectedOutput);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_ModifyPointInCollider)
{
// Verify that for a point inside the collider, the output point contains the correct tag and value.
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
// Output should match the input, but with an added tag / value
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
constexpr bool pointInCollider = true;
TestSurfaceDataColliderModifier({ tag }, input, pointInCollider, expectedOutput);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_DoNotModifyPointOutsideCollider)
{
// Verify that for a point outside the collider, the output point contains no tags / values.
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
// Output should match the input - no extra tags / values should be added.
const char* tag = "test_mask";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal, {});
constexpr bool pointInCollider = true;
TestSurfaceDataColliderModifier({ tag }, input, !pointInCollider, expectedOutput);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_ModifyPointInColliderWithMultipleTags)
{
// Verify that if the component has multiple tags, all of them get put on the output with the same value.
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f), {});
// Output should match the input, but with two added tags
const char* tag1 = "test_mask1";
const char* tag2 = "test_mask2";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(tag1, 1.0f), AZStd::make_pair<AZStd::string, float>(tag2, 1.0f) });
constexpr bool pointInCollider = true;
TestSurfaceDataColliderModifier({ tag1, tag2 }, input, pointInCollider, expectedOutput);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_ModifierPreservesInputTags)
{
// Verify that the output contains input tags that are NOT on the modification list and adds any
// new tags that weren't in the input
// Set arbitrary input data
const char* preservedTag = "preserved_tag";
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f),
{ AZStd::make_pair<AZStd::string, float>(preservedTag, 1.0f) });
// Output should match the input, but with two added tags
const char* modifierTag = "modifier_tag";
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(preservedTag, 1.0f), AZStd::make_pair<AZStd::string, float>(modifierTag, 1.0f) });
constexpr bool pointInCollider = true;
TestSurfaceDataColliderModifier({ modifierTag }, input, pointInCollider, expectedOutput);
}
TEST_F(SurfaceDataTestFixture, SurfaceDataColliderComponent_KeepsHigherValueFromModifier)
{
// Verify that if the input has a lower value on the tag than the modifier, it keeps the higher value.
const char* tag = "test_mask";
// Select an input value that's lower than the collider value
float inputValue = 0.25f;
// Set arbitrary input data
SurfaceData::SurfacePoint input = CreateSurfacePoint(AZ::EntityId(0x12345678), AZ::Vector3(1.0f), AZ::Vector3(0.0f),
{ AZStd::make_pair<AZStd::string, float>(tag, inputValue) });
// Output should match the input, except that the value on the tag gets the higher modifier value
SurfaceData::SurfacePoint expectedOutput = CreateSurfacePoint(input.m_entityId, input.m_position, input.m_normal,
{ AZStd::make_pair<AZStd::string, float>(tag, 1.0f) });
constexpr bool pointInCollider = true;
TestSurfaceDataColliderModifier({ tag }, input, pointInCollider, expectedOutput);
}
}
@@ -0,0 +1,752 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SurfaceData_precompiled.h"
#include <AzTest/AzTest.h>
#include <Mocks/ITimerMock.h>
#include <Mocks/ICryPakMock.h>
#include <Mocks/IConsoleMock.h>
#include <Mocks/ISystemMock.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Math/Random.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/std/chrono/clocks.h>
#include <SurfaceDataSystemComponent.h>
#include <SurfaceDataModule.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <SurfaceData/SurfaceDataModifierRequestBus.h>
#include <SurfaceData/SurfaceTag.h>
#include <SurfaceData/Utility/SurfaceDataUtility.h>
struct MockGlobalEnvironment
{
MockGlobalEnvironment()
{
m_stubEnv.pTimer = &m_stubTimer;
m_stubEnv.pCryPak = &m_stubPak;
m_stubEnv.pConsole = &m_stubConsole;
m_stubEnv.pSystem = &m_stubSystem;
m_stubEnv.p3DEngine = nullptr;
gEnv = &m_stubEnv;
}
~MockGlobalEnvironment()
{
gEnv = nullptr;
}
private:
SSystemGlobalEnvironment m_stubEnv;
testing::NiceMock<TimerMock> m_stubTimer;
testing::NiceMock<CryPakMock> m_stubPak;
testing::NiceMock<ConsoleMock> m_stubConsole;
testing::NiceMock<SystemMock> m_stubSystem;
};
// Simple class for mocking out a surface provider, so that we can control exactly what points we expect to query in our tests.
// This can be used to either provide a surface or modify a surface.
class MockSurfaceProvider
: private SurfaceData::SurfaceDataProviderRequestBus::Handler
, private SurfaceData::SurfaceDataModifierRequestBus::Handler
{
public:
enum class ProviderType
{
SURFACE_PROVIDER,
SURFACE_MODIFIER
};
MockSurfaceProvider(ProviderType providerType, const SurfaceData::SurfaceTagVector& surfaceTags,
AZ::Vector3 start, AZ::Vector3 end, AZ::Vector3 stepSize,
AZ::EntityId id = AZ::EntityId(0x12345678))
{
m_tags = surfaceTags;
m_providerType = providerType;
m_id = id;
SetPoints(start, end, stepSize);
Register();
}
~MockSurfaceProvider()
{
Unregister();
}
private:
AZStd::unordered_map<AZStd::pair<float, float>, SurfaceData::SurfacePointList> m_GetSurfacePoints;
SurfaceData::SurfaceTagVector m_tags;
ProviderType m_providerType;
AZ::EntityId m_id;
void SetPoints(AZ::Vector3 start, AZ::Vector3 end, AZ::Vector3 stepSize)
{
m_GetSurfacePoints.clear();
// Create a set of points that go from start to end (exclusive), with one
// point per step size.
// The XY values create new SurfacePoint entries, the Z values are used to create
// the list of surface points at each XY input point.
for (float y = start.GetY(); y < end.GetY(); y += stepSize.GetY())
{
for (float x = start.GetX(); x < end.GetX(); x += stepSize.GetX())
{
SurfaceData::SurfacePointList points;
for (float z = start.GetZ(); z < end.GetZ(); z += stepSize.GetZ())
{
SurfaceData::SurfacePoint point;
point.m_entityId = m_id;
point.m_position = AZ::Vector3(x, y, z);
point.m_normal = AZ::Vector3::CreateAxisZ();
AddMaxValueForMasks(point.m_masks, m_tags, 1.0f);
points.push_back(point);
}
m_GetSurfacePoints[AZStd::pair<float, float>(x, y)] = points;
}
}
}
AZ::Aabb GetBounds()
{
AZ::Aabb bounds = AZ::Aabb::CreateNull();
for (auto& entry : m_GetSurfacePoints)
{
for (auto& point : entry.second)
{
bounds.AddPoint(point.m_position);
}
}
return bounds;
}
void Register()
{
SurfaceData::SurfaceDataRegistryEntry registryEntry;
registryEntry.m_entityId = m_id;
registryEntry.m_bounds = GetBounds();
registryEntry.m_tags = m_tags;
m_providerHandle = SurfaceData::InvalidSurfaceDataRegistryHandle;
if (m_providerType == ProviderType::SURFACE_PROVIDER)
{
SurfaceData::SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceData::SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataProvider, registryEntry);
SurfaceData::SurfaceDataProviderRequestBus::Handler::BusConnect(m_providerHandle);
}
else
{
SurfaceData::SurfaceDataSystemRequestBus::BroadcastResult(m_providerHandle, &SurfaceData::SurfaceDataSystemRequestBus::Events::RegisterSurfaceDataModifier, registryEntry);
SurfaceData::SurfaceDataModifierRequestBus::Handler::BusConnect(m_providerHandle);
}
}
void Unregister()
{
if (m_providerType == ProviderType::SURFACE_PROVIDER)
{
SurfaceData::SurfaceDataProviderRequestBus::Handler::BusDisconnect();
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataProvider, m_providerHandle);
}
else
{
SurfaceData::SurfaceDataModifierRequestBus::Handler::BusDisconnect();
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::UnregisterSurfaceDataModifier, m_providerHandle);
}
m_providerHandle = SurfaceData::InvalidSurfaceDataRegistryHandle;
}
//////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const override
{
auto surfacePoints = m_GetSurfacePoints.find(AZStd::make_pair(inPosition.GetX(), inPosition.GetY()));
if (surfacePoints != m_GetSurfacePoints.end())
{
for (auto& point : surfacePoints->second)
{
surfacePointList.push_back(point);
}
}
}
//////////////////////////////////////////////////////////////////////////
// SurfaceDataModifierRequestBus
void ModifySurfacePoints(SurfaceData::SurfacePointList& surfacePointList) const override
{
for (auto& point : surfacePointList)
{
auto surfacePoints = m_GetSurfacePoints.find(AZStd::make_pair(point.m_position.GetX(), point.m_position.GetY()));
if (surfacePoints != m_GetSurfacePoints.end())
{
AddMaxValueForMasks(point.m_masks, m_tags, 1.0f);
}
}
}
SurfaceData::SurfaceDataRegistryHandle m_providerHandle = SurfaceData::InvalidSurfaceDataRegistryHandle;
};
TEST(SurfaceDataTest, ComponentsWithComponentApplication)
{
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_FULL;
appDesc.m_stackRecordLevels = 20;
MockGlobalEnvironment mocks;
AZ::ComponentApplication app;
AZ::Entity* systemEntity = app.Create(appDesc);
ASSERT_TRUE(systemEntity != nullptr);
app.RegisterComponentDescriptor(SurfaceData::SurfaceDataSystemComponent::CreateDescriptor());
systemEntity->CreateComponent<SurfaceData::SurfaceDataSystemComponent>();
systemEntity->Init();
systemEntity->Activate();
app.Destroy();
ASSERT_TRUE(true);
}
class SurfaceDataTestApp
: public ::testing::Test
{
public:
SurfaceDataTestApp()
: m_application()
, m_systemEntity(nullptr)
{
}
void SetUp() override
{
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 50 * 1024 * 1024;
appDesc.m_recordingMode = AZ::Debug::AllocationRecords::RECORD_FULL;
appDesc.m_stackRecordLevels = 20;
AZ::ComponentApplication::StartupParameters appStartup;
appStartup.m_createStaticModulesCallback =
[](AZStd::vector<AZ::Module*>& modules)
{
modules.emplace_back(new SurfaceData::SurfaceDataModule);
};
m_systemEntity = m_application.Create(appDesc, appStartup);
m_systemEntity->Init();
m_systemEntity->Activate();
}
void TearDown() override
{
m_application.Destroy();
}
bool ValidateRegionListSize(AZ::Aabb bounds, AZ::Vector2 stepSize, const SurfaceData::SurfacePointListPerPosition& outputList)
{
// We expect the output list to contain width * height output entries.
// The right edge of the AABB should be treated as exclusive, so a 4x4 box with 1 step size will produce 16 entries (0, 1, 2, 3 on each dimension),
// but a 4.1 x 4.1 box with 1 step size will produce 25 entries (0, 1, 2, 3, 4 on each dimension).
return (outputList.size() == aznumeric_cast<size_t>(ceil(bounds.GetXExtent() * stepSize.GetX()) * ceil(bounds.GetYExtent() * stepSize.GetY())));
}
AZ::ComponentApplication m_application;
AZ::Entity* m_systemEntity;
MockGlobalEnvironment m_mocks;
// Test Surface Data tags that we can use for testing query functionality
const AZ::Crc32 m_testSurface1Crc = AZ::Crc32("test_surface1");
const AZ::Crc32 m_testSurface2Crc = AZ::Crc32("test_surface2");
const AZ::Crc32 m_testSurfaceNoMatchCrc = AZ::Crc32("test_surface_no_match");
};
TEST_F(SurfaceDataTestApp, SurfaceData_TestRegisteredTags)
{
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> registeredTags = SurfaceData::SurfaceTag::GetRegisteredTags();
for (const auto& searchTerm : SurfaceData::Constants::s_allTagNames)
{
ASSERT_TRUE(AZStd::find_if(registeredTags.begin(), registeredTags.end(), [searchTerm](decltype(registeredTags)::value_type pair)
{
return pair.second == searchTerm;
}));
}
}
#if AZ_TRAIT_DISABLE_FAILED_SURFACE_DATA_TESTS
TEST_F(SurfaceDataTestApp, DISABLED_SurfaceData_TestGetQuadListRayIntersection)
#else
TEST_F(SurfaceDataTestApp, SurfaceData_TestGetQuadListRayIntersection)
#endif // AZ_TRAIT_DISABLE_FAILED_SURFACE_DATA_TESTS
{
AZStd::vector<AZ::Vector3> quads;
AZ::Vector3 outPosition;
AZ::Vector3 outNormal;
bool result;
struct RayTest
{
// Input quad
AZ::Vector3 quadVertices[4];
// Input ray
AZ::Vector3 rayOrigin;
AZ::Vector3 rayDirection;
float rayMaxRange;
// Expected outputs
bool expectedResult;
AZ::Vector3 expectedOutPosition;
AZ::Vector3 expectedOutNormal;
};
RayTest tests[] =
{
// Ray intersects quad
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(100.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 100.0f, 0.0f), AZ::Vector3(100.0f, 100.0f, 0.0f)},
AZ::Vector3(50.0f, 50.0f, 10.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), 20.0f, true, AZ::Vector3(50.0f, 50.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 1.0f)},
// Ray not long enough to intersect
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(100.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 100.0f, 0.0f), AZ::Vector3(100.0f, 100.0f, 0.0f)},
AZ::Vector3(50.0f, 50.0f, 10.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), 5.0f, false, AZ::Vector3( 0.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 0.0f)},
// 0-length ray on quad surface
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(100.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 100.0f, 0.0f), AZ::Vector3(100.0f, 100.0f, 0.0f)},
AZ::Vector3(50.0f, 50.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, -1.0f), 0.0f, true, AZ::Vector3(50.0f, 50.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 1.0f)},
// ray in wrong direction
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(100.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 100.0f, 0.0f), AZ::Vector3(100.0f, 100.0f, 0.0f)},
AZ::Vector3(50.0f, 50.0f, 10.0f), AZ::Vector3(0.0f, 0.0f, 1.0f), 20.0f, false, AZ::Vector3( 0.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 0.0f)},
// The following tests are specific cases that worked differently when the implementation of GetQuadRayListIntersection used AZ::Intersect::IntersectRayQuad
// instead of IntersectSegmentTriangle. We'll keep them here both as good non-trivial tests and to ensure that if anyone ever tries to change the implmentation,
// they can easily validate whether or not IntersectRayQuad will produce the same results.
// ray passes IntersectSegmentTriangle but fails IntersectRayQuad
{{AZ::Vector3(499.553, 688.946, 48.788), AZ::Vector3(483.758, 698.655, 48.788), AZ::Vector3(498.463, 687.181, 48.916), AZ::Vector3(482.701, 696.942, 48.916)},
AZ::Vector3(485.600, 695.200, 49.501), AZ::Vector3(-0.000f, -0.000f, -1.000f), 18.494f, true, AZ::Vector3(485.600, 695.200, 48.913), AZ::Vector3(0.033, 0.053, 0.998)},
// ray fails IntersectSegmentTriangle but passes IntersectRayQuad
// IntersectRayQuad hits with the following position / normal: AZ::Vector3(480.000, 688.800, 49.295), AZ::Vector3(0.020, 0.032, 0.999)
{{AZ::Vector3(495.245, 681.984, 49.218), AZ::Vector3(479.450, 691.692, 49.218), AZ::Vector3(494.205, 680.282, 49.292), AZ::Vector3(478.356, 689.902, 49.292)},
AZ::Vector3(480.000, 688.800, 49.501), AZ::Vector3(-0.000, -0.000, -1.000), 18.494f, false, AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(0.0f, 0.0f, 0.0f)},
// ray passes IntersectSegmentTriangle and IntersectRayQuad, but hits at different positions
// IntersectRayQuad hits with the following position / normal: AZ::Vector3(498.400, 700.000, 48.073), AZ::Vector3(0.046, 0.085, 0.995)
{{AZ::Vector3(504.909, 698.078, 47.913), AZ::Vector3(488.641, 706.971, 47.913), AZ::Vector3(503.867, 696.206, 48.121), AZ::Vector3(487.733, 705.341, 48.121)},
AZ::Vector3(498.400, 700.000, 49.501), AZ::Vector3(-0.000f, -0.000f, -1.000f), 53.584f, true, AZ::Vector3(498.400, 700.000, 48.062), AZ::Vector3(0.048, 0.084, 0.995)},
// ray passes IntersectSegmentTriangle and IntersectRayQuad, but hits at different normals
// IntersectRayQuad hits with the following position / normal: AZ::Vector3(492.800, 703.200, 48.059), AZ::Vector3(0.046, 0.085, 0.995)
{{AZ::Vector3(504.909, 698.078, 47.913), AZ::Vector3(488.641, 706.971, 47.913), AZ::Vector3(503.867, 696.206, 48.121), AZ::Vector3(487.733, 705.341, 48.121)},
AZ::Vector3(492.800, 703.200, 49.501), AZ::Vector3(-0.000f, -0.000f, -1.000f), 18.494f, true, AZ::Vector3(492.800, 703.200, 48.059), AZ::Vector3(0.053, 0.097, 0.994)},
};
for (const auto &test : tests)
{
quads.clear();
outPosition.Set(0.0f, 0.0f, 0.0f);
outNormal.Set(0.0f, 0.0f, 0.0f);
quads.push_back(test.quadVertices[0]);
quads.push_back(test.quadVertices[1]);
quads.push_back(test.quadVertices[2]);
quads.push_back(test.quadVertices[3]);
result = SurfaceData::GetQuadListRayIntersection(quads, test.rayOrigin, test.rayDirection, test.rayMaxRange, outPosition, outNormal);
ASSERT_TRUE(result == test.expectedResult);
if (result || test.expectedResult)
{
ASSERT_TRUE(outPosition.IsClose(test.expectedOutPosition));
ASSERT_TRUE(outNormal.IsClose(test.expectedOutNormal));
}
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestAabbOverlaps2D)
{
// Test to make sure the utility method "AabbOverlaps2D" functions as expected.
struct TestCase
{
enum TestIndex
{
SOURCE_MIN,
SOURCE_MAX,
DEST_MIN,
DEST_MAX,
NUM_PARAMS
};
AZ::Vector3 m_testData[NUM_PARAMS];
bool m_overlaps;
};
TestCase testCases[]
{
// Overlap=TRUE Boxes fully overlap in 3D space
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(1.0f, 1.0f, 1.0f), AZ::Vector3(3.0f, 3.0f, 3.0f)}, true },
// Overlap=TRUE Boxes overlap in 2D space, but not 3D
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(1.0f, 1.0f, 4.0f), AZ::Vector3(3.0f, 3.0f, 6.0f)}, true},
// Overlap=TRUE Boxes are equal
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f)}, true},
// Overlap=TRUE Box contains other box
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(1.0f, 1.0f, 1.0f), AZ::Vector3(1.5f, 1.5f, 1.5f)}, true },
// Overlap=FALSE Boxes only overlap in X and Z, not Y
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(1.0f, 4.0f, 1.0f), AZ::Vector3(3.0f, 6.0f, 3.0f)}, false},
// Overlap=FALSE Boxes only overlap in Y and Z, not X
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(4.0f, 1.0f, 1.0f), AZ::Vector3(6.0f, 3.0f, 3.0f)}, false },
};
for (auto& testCase : testCases)
{
AZ::Aabb box1 = AZ::Aabb::CreateFromMinMax(testCase.m_testData[TestCase::SOURCE_MIN], testCase.m_testData[TestCase::SOURCE_MAX]);
AZ::Aabb box2 = AZ::Aabb::CreateFromMinMax(testCase.m_testData[TestCase::DEST_MIN], testCase.m_testData[TestCase::DEST_MAX]);
// Make sure the test produces the correct result.
// Also make sure it's correct regardless of which order the boxes are passed in.
EXPECT_TRUE(SurfaceData::AabbOverlaps2D(box1, box2) == testCase.m_overlaps);
EXPECT_TRUE(SurfaceData::AabbOverlaps2D(box2, box1) == testCase.m_overlaps);
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestAabbContains2D)
{
// Test to make sure the utility method "AabbContains2D" functions as expected.
struct TestCase
{
enum TestIndex
{
BOX_MIN,
BOX_MAX,
POINT,
NUM_PARAMS
};
AZ::Vector3 m_testData[NUM_PARAMS];
bool m_contains;
};
TestCase testCases[]
{
// Contains=TRUE Box and point fully overlap in 3D space
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(1.0f, 1.0f, 1.0f)}, true},
// Contains=TRUE Box and point overlap in 2D space, but not 3D
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(1.0f, 1.0f, 4.0f)}, true},
// Contains=TRUE Point on box min corner
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(0.0f, 0.0f, 0.0f)}, true },
// Contains=TRUE Point on box max corner
{{AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(2.0f, 2.0f, 2.0f)}, true},
// Contains=FALSE Box and point only overlap in X and Z, not Y
{{ AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(1.0f, 4.0f, 1.0f) }, false},
// Contains=FALSE Box and point only overlap in Y and Z, not X
{{ AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(4.0f, 1.0f, 1.0f) }, false},
// Contains=FALSE Box and point don't overlap at all
{{ AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(2.0f, 2.0f, 2.0f), AZ::Vector3(4.0f, 4.0f, 4.0f) }, false},
};
for (auto& testCase : testCases)
{
AZ::Aabb box = AZ::Aabb::CreateFromMinMax(testCase.m_testData[TestCase::BOX_MIN], testCase.m_testData[TestCase::BOX_MAX]);
AZ::Vector3& point = testCase.m_testData[TestCase::POINT];
// Make sure the test produces the correct result.
EXPECT_TRUE(SurfaceData::AabbContains2D(box, point) == testCase.m_contains);
// Test the Vector2 version as well.
EXPECT_TRUE(SurfaceData::AabbContains2D(box, AZ::Vector2(point.GetX(), point.GetY())) == testCase.m_contains);
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion)
{
// This tests the basic functionality of GetSurfacePointsFromRegion:
// - The surface points are queried by stepping through an AABB, which is inclusive on one side, and exclusive on the other.
// i.e. (0,0) - (4,4) will include (0,0), but exclude (4,4)
// - The Z range of the input region is ignored when querying for points. (This is consistent with GetSurfacePoints)
// - The output has one list entry per surface point queried
// - The output has the correct expected points and masks
// Create a mock Surface Provider that covers from (0, 0) - (8, 8) in space.
// It defines points spaced 0.25 apart, with heights of 0 and 4, and with the tags "test_surface1" and "test_surface2".
// (We're creating points spaced more densely than we'll query just to verify that we only get back the queried points)
SurfaceData::SurfaceTagVector providerTags = { SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) };
MockSurfaceProvider mockProvider(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, providerTags,
AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(0.25f, 0.25f, 4.0f));
// Query for all the surface points from (0, 0, 16) - (4, 4, 16) with a step size of 1.
// Note that the Z range is deliberately chosen to be outside the surface provider range to demonstrate
// that it is ignored when selecting points.
SurfaceData::SurfacePointListPerPosition availablePointsPerPosition;
AZ::Vector2 stepSize(1.0f, 1.0f);
AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f, 0.0f, 16.0f), AZ::Vector3(4.0f, 4.0f, 16.0f));
SurfaceData::SurfaceTagVector testTags = providerTags;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromRegion,
regionBounds, stepSize, testTags, availablePointsPerPosition);
EXPECT_TRUE(ValidateRegionListSize(regionBounds, stepSize, availablePointsPerPosition));
// We expect every entry in the output list to have two surface points, at heights 0 and 4, sorted in
// decreasing height order. The XY positions should match the query positions, and the masks list should
// be the same size as the set of masks the provider owns. We *could* check every mask as well for completeness,
// but that seems like overkill.
for (auto& queryPosition : availablePointsPerPosition)
{
const SurfaceData::SurfacePointList& pointList = queryPosition.second;
EXPECT_TRUE(pointList.size() == 2);
EXPECT_TRUE(pointList[0].m_position.GetZ() == 4.0f);
EXPECT_TRUE(pointList[1].m_position.GetZ() == 0.0f);
for (auto& point : pointList)
{
EXPECT_TRUE(queryPosition.first.GetX() == point.m_position.GetX());
EXPECT_TRUE(queryPosition.first.GetY() == point.m_position.GetY());
EXPECT_TRUE(point.m_masks.size() == providerTags.size());
}
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingMasks)
{
// This test verifies that if we query surfaces with a non-matching mask, the points will get filtered out.
// Create a mock Surface Provider that covers from (0, 0) - (8, 8) in space.
// It defines points spaced 0.25 apart, with heights of 0 and 4, and with the tags "test_surface1" and "test_surface2".
SurfaceData::SurfaceTagVector providerTags = { SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) };
MockSurfaceProvider mockProvider(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, providerTags,
AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(0.25f, 0.25f, 4.0f));
// Query for all the surface points from (0, 0, 0) - (4, 4, 4) with a step size of 1.
// We only include a surface tag that does NOT exist in the surface provider.
SurfaceData::SurfacePointListPerPosition availablePointsPerPosition;
AZ::Vector2 stepSize(1.0f, 1.0f);
AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f));
SurfaceData::SurfaceTagVector testTags = { SurfaceData::SurfaceTag(m_testSurfaceNoMatchCrc) };
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromRegion,
regionBounds, stepSize, testTags, availablePointsPerPosition);
EXPECT_TRUE(ValidateRegionListSize(regionBounds, stepSize, availablePointsPerPosition));
// We expect every entry in the output list to have no surface points, since the requested mask doesn't match
// any of the masks from our mock surface provider.
for (auto& queryPosition : availablePointsPerPosition)
{
EXPECT_TRUE(queryPosition.second.size() == 0);
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_NoMatchingRegion)
{
// This test verifies that if we query surfaces with a non-overlapping region, no points are returned.
// Create a mock Surface Provider that covers from (0,0) - (8, 8) in space.
// It defines points spaced 0.25 apart, with heights of 0 and 4, and with the tags "test_surface1" and "test_surface2".
SurfaceData::SurfaceTagVector providerTags = { SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) };
MockSurfaceProvider mockProvider(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, providerTags,
AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(0.25f, 0.25f, 4.0f));
// Query for all the surface points from (16, 16) - (20, 20) with a step size of 1.
SurfaceData::SurfacePointListPerPosition availablePointsPerPosition;
AZ::Vector2 stepSize(1.0f, 1.0f);
AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(16.0f), AZ::Vector3(20.0f));
SurfaceData::SurfaceTagVector testTags = providerTags;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromRegion,
regionBounds, stepSize, testTags, availablePointsPerPosition);
EXPECT_TRUE(ValidateRegionListSize(regionBounds, stepSize, availablePointsPerPosition));
// We expect every entry in the output list to have no surface points, since the input points don't overlap with
// our surface provider.
for (auto& queryPosition : availablePointsPerPosition)
{
const SurfaceData::SurfacePointList& pointList = queryPosition.second;
EXPECT_TRUE(pointList.size() == 0);
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_ProviderModifierMasksCombine)
{
// This test verifies that SurfaceDataModifiers can successfully modify the tags on each point.
// It also verifies that points won't be dropped from the results as long as either the provider
// or the modifier add the correct tag to the point.
// Create a mock Surface Provider that covers from (0,0) - (8, 8) in space.
// It defines points spaced 1 apart, with heights of 0 and 4, and with the tag "test_surface1".
SurfaceData::SurfaceTagVector providerTags = { SurfaceData::SurfaceTag(m_testSurface1Crc) };
MockSurfaceProvider mockProvider(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, providerTags,
AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(1.0f, 1.0f, 4.0f));
// Create a mock Surface Modifier that covers from (0,0) - (8, 8) in space.
// It will modify points spaced 1 apart, with heights of 0 and 4, and add the tag "test_surface2".
SurfaceData::SurfaceTagVector modifierTags = { SurfaceData::SurfaceTag(m_testSurface2Crc) };
MockSurfaceProvider mockModifier(MockSurfaceProvider::ProviderType::SURFACE_MODIFIER, modifierTags,
AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(1.0f, 1.0f, 4.0f));
// Query for all the surface points from (0, 0) - (4, 4) with a step size of 1.
// We perform this test 3 times - once with just the provider tag, once with just the modifier tag,
// and once with both. We expect identical results on each test, since each point should get both
// the provider and the modifier tag.
SurfaceData::SurfaceTagVector tagTests[] =
{
{ SurfaceData::SurfaceTag(m_testSurface1Crc) },
{ SurfaceData::SurfaceTag(m_testSurface2Crc) },
{ SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) },
};
for (auto& tagTest : tagTests)
{
SurfaceData::SurfacePointListPerPosition availablePointsPerPosition;
AZ::Vector2 stepSize(1.0f, 1.0f);
AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f));
SurfaceData::SurfaceTagVector testTags = tagTest;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromRegion,
regionBounds, stepSize, testTags, availablePointsPerPosition);
EXPECT_TRUE(ValidateRegionListSize(regionBounds, stepSize, availablePointsPerPosition));
// We expect every entry in the output list to have two surface points (with heights 0 and 4),
// and each point should have both the "test_surface1" and "test_surface2" tag.
for (auto& queryPosition : availablePointsPerPosition)
{
const SurfaceData::SurfacePointList& pointList = queryPosition.second;
EXPECT_TRUE(pointList.size() == 2);
for (auto& point : pointList)
{
EXPECT_TRUE(point.m_masks.size() == 2);
}
}
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_SimilarPointsMergeTogether)
{
// This test verifies that if two separate providers create points at very similar heights, the
// points will get merged together in the results, with the resulting point ending up with both
// sets of tags.
// Create two mock Surface Providers that covers from (0, 0) - (8, 8) in space, with points spaced 0.25 apart.
// The first has heights 0 and 4, with the tag "surfaceTag1". The second has heights 0.005 and 4.005, with the tag "surfaceTag2".
SurfaceData::SurfaceTagVector provider1Tags = { SurfaceData::SurfaceTag(m_testSurface1Crc) };
MockSurfaceProvider mockProvider1(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, provider1Tags,
AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(0.25f, 0.25f, 4.0f),
AZ::EntityId(0x11111111));
SurfaceData::SurfaceTagVector provider2Tags = { SurfaceData::SurfaceTag(m_testSurface2Crc) };
MockSurfaceProvider mockProvider2(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, provider2Tags,
AZ::Vector3(0.0f, 0.0f, 0.0f + (AZ::Constants::Tolerance / 2.0f)),
AZ::Vector3(8.0f, 8.0f, 8.0f + (AZ::Constants::Tolerance / 2.0f)),
AZ::Vector3(0.25f, 0.25f, 4.0f),
AZ::EntityId(0x22222222));
// Query for all the surface points from (0, 0) - (4, 4) with a step size of 1.
SurfaceData::SurfacePointListPerPosition availablePointsPerPosition;
AZ::Vector2 stepSize(1.0f, 1.0f);
AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f));
SurfaceData::SurfaceTagVector testTags = { SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) };
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromRegion,
regionBounds, stepSize, testTags, availablePointsPerPosition);
EXPECT_TRUE(ValidateRegionListSize(regionBounds, stepSize, availablePointsPerPosition));
// We expect every entry in the output list to have two surface points, not four. The two points
// should have both surface tags on them.
for (auto& queryPosition : availablePointsPerPosition)
{
const SurfaceData::SurfacePointList& pointList = queryPosition.second;
EXPECT_TRUE(pointList.size() == 2);
for (auto& point : pointList)
{
EXPECT_TRUE(point.m_masks.size() == 2);
}
}
}
TEST_F(SurfaceDataTestApp, SurfaceData_TestSurfacePointsFromRegion_DissimilarPointsDoNotMergeTogether)
{
// This test verifies that if two separate providers create points at dissimilar heights, the
// points will NOT get merged together in the results.
// Create two mock Surface Providers that covers from (0, 0) - (8, 8) in space, with points spaced 0.25 apart.
// The first has heights 0 and 4, with the tag "surfaceTag1". The second has heights 0.02 and 4.02, with the tag "surfaceTag2".
SurfaceData::SurfaceTagVector provider1Tags = { SurfaceData::SurfaceTag(m_testSurface1Crc) };
MockSurfaceProvider mockProvider1(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, provider1Tags,
AZ::Vector3(0.0f), AZ::Vector3(8.0f), AZ::Vector3(0.25f, 0.25f, 4.0f),
AZ::EntityId(0x11111111));
SurfaceData::SurfaceTagVector provider2Tags = { SurfaceData::SurfaceTag(m_testSurface2Crc) };
MockSurfaceProvider mockProvider2(MockSurfaceProvider::ProviderType::SURFACE_PROVIDER, provider2Tags,
AZ::Vector3(0.0f, 0.0f, 0.0f + (AZ::Constants::Tolerance * 2.0f)),
AZ::Vector3(8.0f, 8.0f, 8.0f + (AZ::Constants::Tolerance * 2.0f)),
AZ::Vector3(0.25f, 0.25f, 4.0f),
AZ::EntityId(0x22222222));
// Query for all the surface points from (0, 0) - (4, 4) with a step size of 1.
SurfaceData::SurfacePointListPerPosition availablePointsPerPosition;
AZ::Vector2 stepSize(1.0f, 1.0f);
AZ::Aabb regionBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(4.0f));
SurfaceData::SurfaceTagVector testTags = { SurfaceData::SurfaceTag(m_testSurface1Crc), SurfaceData::SurfaceTag(m_testSurface2Crc) };
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromRegion,
regionBounds, stepSize, testTags, availablePointsPerPosition);
EXPECT_TRUE(ValidateRegionListSize(regionBounds, stepSize, availablePointsPerPosition));
// We expect every entry in the output list to have four surface points with one tag each,
// because the points are far enough apart that they won't merge.
for (auto& queryPosition : availablePointsPerPosition)
{
const SurfaceData::SurfacePointList& pointList = queryPosition.second;
EXPECT_TRUE(pointList.size() == 4);
for (auto& point : pointList)
{
EXPECT_TRUE(point.m_masks.size() == 1);
}
}
}
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
@@ -0,0 +1,27 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
Source/SurfaceDataEditorModule.cpp
Source/SurfaceDataEditorModule.h
Source/Editor/EditorSurfaceTagListAsset.cpp
Source/Editor/EditorSurfaceTagListAsset.h
Source/Editor/EditorSurfaceDataSystemComponent.cpp
Source/Editor/EditorSurfaceDataSystemComponent.h
Source/Editor/EditorSurfaceDataColliderComponent.cpp
Source/Editor/EditorSurfaceDataColliderComponent.h
Source/Editor/EditorSurfaceDataMeshComponent.cpp
Source/Editor/EditorSurfaceDataMeshComponent.h
Source/Editor/EditorSurfaceDataShapeComponent.cpp
Source/Editor/EditorSurfaceDataShapeComponent.h
Source/SurfaceDataModule.cpp
Source/SurfaceDataModule.h
)
@@ -0,0 +1,36 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
Source/SurfaceData_precompiled.cpp
Source/SurfaceData_precompiled.h
Include/SurfaceData/SurfaceDataConstants.h
Include/SurfaceData/SurfaceDataTypes.h
Include/SurfaceData/SurfaceDataSystemRequestBus.h
Include/SurfaceData/SurfaceDataSystemNotificationBus.h
Include/SurfaceData/SurfaceDataTagEnumeratorRequestBus.h
Include/SurfaceData/SurfaceDataTagProviderRequestBus.h
Include/SurfaceData/SurfaceDataProviderRequestBus.h
Include/SurfaceData/SurfaceDataModifierRequestBus.h
Include/SurfaceData/SurfaceTag.h
Include/SurfaceData/Utility/SurfaceDataUtility.h
Source/SurfaceDataSystemComponent.cpp
Source/SurfaceDataSystemComponent.h
Source/TerrainSurfaceDataSystemComponent.cpp
Source/TerrainSurfaceDataSystemComponent.h
Source/SurfaceTag.cpp
Source/Components/SurfaceDataColliderComponent.cpp
Source/Components/SurfaceDataColliderComponent.h
Source/Components/SurfaceDataMeshComponent.cpp
Source/Components/SurfaceDataMeshComponent.h
Source/Components/SurfaceDataShapeComponent.cpp
Source/Components/SurfaceDataShapeComponent.h
)
@@ -0,0 +1,15 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
Source/SurfaceDataModule.h
Source/SurfaceDataModule.cpp
)
@@ -0,0 +1,18 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
Include/SurfaceData/Tests/SurfaceDataTestMocks.h
Tests/SurfaceDataColliderComponentTest.cpp
Tests/SurfaceDataTest.cpp
Source/SurfaceDataModule.cpp
Source/SurfaceDataModule.h
)
+29
View File
@@ -0,0 +1,29 @@
{
"GemFormatVersion": 4,
"Uuid": "5de82d29d6094bfe97c1a4d35fcd5fbe",
"Name": "SurfaceData",
"DisplayName": "Surface Data [PREVIEW]",
"Version": "0.1.0",
"Summary": "A Lumberyard Gem that allows you to attach user-defined tags to surfaces and volumes, and fetch data like positions, normals, and tags for those surfaces.",
"Tags": ["Surface", "Geometry", "Tags"],
"IconPath": "preview.png",
"Modules": [
{
"Type": "GameModule"
},
{
"Name": "Editor",
"Type": "EditorModule",
"Extends": "GameModule"
}
],
"Dependencies": [
{
"Uuid": "ff06785f7145416b9d46fde39098cb0c",
"VersionConstraints": [
"~>0.1"
],
"_comment": "LmbrCentral"
}
]
}
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f96f7e9f08f07fca3d72283759ab23677f9c54c0f18069992b02fdf0d19630d3
size 36469