Merge branch 'development' of https://github.com/o3de/o3de into cgalvan/RemovedGradientImageBuilder

monroegm-disable-blank-issue-2
Chris Galvan 4 years ago
commit 283fe216dc

@ -12,7 +12,7 @@ import pytest
import editor_python_test_tools.hydra_test_utils as hydra import editor_python_test_tools.hydra_test_utils as hydra
import ly_test_tools.environment.file_system as file_system import ly_test_tools.environment.file_system as file_system
from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorTestSuite
from Atom.atom_utils.atom_component_helper import compare_screenshot_to_golden_image, golden_images_directory from Atom.atom_utils.atom_component_helper import compare_screenshot_to_golden_image, golden_images_directory
DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots' DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots'
@ -23,63 +23,92 @@ logger = logging.getLogger(__name__)
@pytest.mark.parametrize("launcher_platform", ['windows_editor']) @pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite): class TestAutomation(EditorTestSuite):
# Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests. # Remove -autotest_mode from global_extra_cmdline_args since we need rendering for these tests.
global_extra_cmdline_args = ["-BatchMode"] # Default is ["-BatchMode", "-autotest_mode"] global_extra_cmdline_args = ["-autotest_mode"] # Default is ["-BatchMode", "-autotest_mode"]
use_null_renderer = False # Default is True
enable_prefab_system = False enable_prefab_system = False
@pytest.mark.test_case_id("C34603773") @staticmethod
class AtomGPU_BasicLevelSetup_SetsUpLevel(EditorSharedTest): def screenshot_setup(screenshot_directory, screenshot_names):
use_null_renderer = False # Default is True """
screenshot_name = "AtomBasicLevelSetup.ppm" :param screenshot_names: list of screenshot file names with extensions
test_screenshots = [] # Gets set by setup() :return: tuple test_screenshots, golden_images each a list of full file paths
screenshot_directory = "" # Gets set by setup() """
test_screenshots = []
# Clear existing test screenshots before starting test. golden_images = []
def setup(self, workspace): for screenshot in screenshot_names:
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) screenshot_path = os.path.join(screenshot_directory, screenshot)
test_screenshots = [os.path.join(screenshot_directory, self.screenshot_name)] test_screenshots.append(screenshot_path)
file_system.delete(test_screenshots, True, True) file_system.delete(test_screenshots, True, True)
for golden_image in screenshot_names:
golden_image_path = os.path.join(golden_images_directory(), golden_image)
golden_images.append(golden_image_path)
return test_screenshots, golden_images
golden_images = [os.path.join(golden_images_directory(), screenshot_name)]
from Atom.tests import hydra_AtomGPU_BasicLevelSetup as test_module @pytest.mark.test_case_id("C34525095")
class AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages_DX12(EditorSingleTest):
from Atom.tests import hydra_AtomGPU_AreaLightScreenshotTest as test_module
assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True extra_cmdline_args = ["-rhi=dx12"]
@pytest.mark.test_case_id("C34525095") # Custom setup/teardown to remove old screenshots and establish paths to golden images
class AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages(EditorSharedTest): def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
use_null_renderer = False # Default is True self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
screenshot_names = [ self.screenshot_names = [
"AreaLight_1.ppm", "AreaLight_1.ppm",
"AreaLight_2.ppm", "AreaLight_2.ppm",
"AreaLight_3.ppm", "AreaLight_3.ppm",
"AreaLight_4.ppm", "AreaLight_4.ppm",
"AreaLight_5.ppm", "AreaLight_5.ppm",
] ]
test_screenshots = [] # Gets set by setup() self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup(
screenshot_directory = "" # Gets set by setup() screenshot_directory=self.screenshot_directory,
screenshot_names=self.screenshot_names)
# Clear existing test screenshots before starting test.
def setup(self, workspace):
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
for screenshot in self.screenshot_names:
screenshot_path = os.path.join(screenshot_directory, screenshot)
self.test_screenshots.append(screenshot_path)
file_system.delete(self.test_screenshots, True, True)
golden_images = [] def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform):
for golden_image in screenshot_names: yield
golden_image_path = os.path.join(golden_images_directory(), golden_image) assert compare_screenshot_to_golden_image(self.screenshot_directory,
golden_images.append(golden_image_path) self.test_screenshots,
self.golden_images,
similarity_threshold=0.96) is True
@pytest.mark.test_case_id("C34525095")
class AtomGPU_LightComponent_AreaLightScreenshotsMatchGoldenImages_Vulkan(EditorSingleTest):
from Atom.tests import hydra_AtomGPU_AreaLightScreenshotTest as test_module from Atom.tests import hydra_AtomGPU_AreaLightScreenshotTest as test_module
assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True extra_cmdline_args = ["-rhi=vulkan"]
# Custom setup/teardown to remove old screenshots and establish paths to golden images
def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
self.screenshot_names = [
"AreaLight_1.ppm",
"AreaLight_2.ppm",
"AreaLight_3.ppm",
"AreaLight_4.ppm",
"AreaLight_5.ppm",
]
self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup(
screenshot_directory=self.screenshot_directory,
screenshot_names=self.screenshot_names)
def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform):
yield
assert compare_screenshot_to_golden_image(self.screenshot_directory,
self.test_screenshots,
self.golden_images,
similarity_threshold=0.96) is True
@pytest.mark.test_case_id("C34525110") @pytest.mark.test_case_id("C34525110")
class AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages(EditorSharedTest): class AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages_DX12(EditorSingleTest):
use_null_renderer = False # Default is True from Atom.tests import hydra_AtomGPU_SpotLightScreenshotTest as test_module
screenshot_names = [
extra_cmdline_args = ["-rhi=dx12"]
# Custom setup/teardown to remove old screenshots and establish paths to golden images
def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
self.screenshot_names = [
"SpotLight_1.ppm", "SpotLight_1.ppm",
"SpotLight_2.ppm", "SpotLight_2.ppm",
"SpotLight_3.ppm", "SpotLight_3.ppm",
@ -87,25 +116,44 @@ class TestAutomation(EditorTestSuite):
"SpotLight_5.ppm", "SpotLight_5.ppm",
"SpotLight_6.ppm", "SpotLight_6.ppm",
] ]
test_screenshots = [] # Gets set by setup() self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup(
screenshot_directory = "" # Gets set by setup() screenshot_directory=self.screenshot_directory,
screenshot_names=self.screenshot_names)
# Clear existing test screenshots before starting test. def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform):
def setup(self, workspace): yield
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH) assert compare_screenshot_to_golden_image(self.screenshot_directory,
for screenshot in self.screenshot_names: self.test_screenshots,
screenshot_path = os.path.join(screenshot_directory, screenshot) self.golden_images,
self.test_screenshots.append(screenshot_path) similarity_threshold=0.96) is True
file_system.delete(self.test_screenshots, True, True)
golden_images = []
for golden_image in screenshot_names:
golden_image_path = os.path.join(golden_images_directory(), golden_image)
golden_images.append(golden_image_path)
@pytest.mark.test_case_id("C34525110")
class AtomGPU_LightComponent_SpotLightScreenshotsMatchGoldenImages_Vulkan(EditorSingleTest):
from Atom.tests import hydra_AtomGPU_SpotLightScreenshotTest as test_module from Atom.tests import hydra_AtomGPU_SpotLightScreenshotTest as test_module
assert compare_screenshot_to_golden_image(screenshot_directory, test_screenshots, golden_images, 0.99) is True extra_cmdline_args = ["-rhi=vulkan"]
# Custom setup/teardown to remove old screenshots and establish paths to golden images
def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
self.screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
self.screenshot_names = [
"SpotLight_1.ppm",
"SpotLight_2.ppm",
"SpotLight_3.ppm",
"SpotLight_4.ppm",
"SpotLight_5.ppm",
"SpotLight_6.ppm",
]
self.test_screenshots, self.golden_images = TestAutomation.screenshot_setup(
screenshot_directory=self.screenshot_directory,
screenshot_names=self.screenshot_names)
def wrap_run(self, request, workspace, editor, editor_test_results, launcher_platform):
yield
assert compare_screenshot_to_golden_image(self.screenshot_directory,
self.test_screenshots,
self.golden_images,
similarity_threshold=0.96) is True
@pytest.mark.parametrize('rhi', ['dx12', 'vulkan']) @pytest.mark.parametrize('rhi', ['dx12', 'vulkan'])

@ -86,9 +86,8 @@ def compare_screenshot_similarity(
if create_zip_archive: if create_zip_archive:
create_screenshots_archive(screenshot_directory) create_screenshots_archive(screenshot_directory)
result = ( result = (
f"When comparing the test_screenshot: '{test_screenshot}' " f"When comparing the test_screenshot: '{test_screenshot}' to golden_image: '{golden_image}'.\n"
f"to golden_image: '{golden_image}' the mean similarity of '{mean_similarity}' " f"The mean similarity ({mean_similarity}) was lower than the similarity threshold ({similarity_threshold})")
f"was lower than the similarity threshold of '{similarity_threshold}'. ")
return result return result
@ -123,7 +122,9 @@ def initial_viewport_setup(screen_width=1280, screen_height=720):
import azlmbr.legacy.general as general import azlmbr.legacy.general as general
general.set_viewport_size(screen_width, screen_height) general.set_viewport_size(screen_width, screen_height)
general.idle_wait_frames(1)
general.update_viewport() general.update_viewport()
general.idle_wait_frames(1)
def enter_exit_game_mode_take_screenshot(screenshot_name, enter_game_tuple, exit_game_tuple, timeout_in_seconds=4): def enter_exit_game_mode_take_screenshot(screenshot_name, enter_game_tuple, exit_game_tuple, timeout_in_seconds=4):
@ -137,13 +138,18 @@ def enter_exit_game_mode_take_screenshot(screenshot_name, enter_game_tuple, exit
""" """
import azlmbr.legacy.general as general import azlmbr.legacy.general as general
from editor_python_test_tools.utils import TestHelper from editor_python_test_tools.utils import TestHelper, Report
from Atom.atom_utils.screenshot_utils import ScreenshotHelper from Atom.atom_utils.screenshot_utils import ScreenshotHelper
screenshot_helper = ScreenshotHelper(general.idle_wait_frames)
TestHelper.enter_game_mode(enter_game_tuple) TestHelper.enter_game_mode(enter_game_tuple)
TestHelper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds) TestHelper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds)
ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(screenshot_name) screenshot_helper.prepare_viewport_for_screenshot(1920, 1080)
success_screenshot = TestHelper.wait_for_condition(
function=lambda: screenshot_helper.capture_screenshot_blocking(screenshot_name),
timeout_in_seconds=timeout_in_seconds)
Report.result(("Screenshot taken", "Screenshot failed to be taken"), success_screenshot)
TestHelper.exit_game_mode(exit_game_tuple) TestHelper.exit_game_mode(exit_game_tuple)
TestHelper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds) TestHelper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=timeout_in_seconds)

@ -107,10 +107,8 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component. 18. Add Mesh component to Sphere Entity and set the Mesh Asset property for the Mesh component.
19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component. 19. Create a Camera Entity as a child entity of the Default Level Entity then add a Camera component.
20. Set the Camera Entity rotation value and set the Camera component Field of View value. 20. Set the Camera Entity rotation value and set the Camera component Field of View value.
21. Enter game mode. 21. Enter/Exit game mode taking screenshot.
22. Take screenshot. 22. Look for errors.
23. Exit game mode.
24. Look for errors.
:return: None :return: None
""" """
@ -127,7 +125,7 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
from Atom.atom_utils.atom_constants import AtomComponentProperties from Atom.atom_utils.atom_constants import AtomComponentProperties
from Atom.atom_utils.atom_component_helper import initial_viewport_setup from Atom.atom_utils.atom_component_helper import initial_viewport_setup
from Atom.atom_utils.screenshot_utils import ScreenshotHelper from Atom.atom_utils.atom_component_helper import enter_exit_game_mode_take_screenshot
DEGREE_RADIAN_FACTOR = 0.0174533 DEGREE_RADIAN_FACTOR = 0.0174533
SCREENSHOT_NAME = "AtomBasicLevelSetup" SCREENSHOT_NAME = "AtomBasicLevelSetup"
@ -300,18 +298,10 @@ def AtomGPU_BasicLevelSetup_SetsUpLevel():
Report.result(Tests.camera_fov_set, camera_component.get_component_property_value( Report.result(Tests.camera_fov_set, camera_component.get_component_property_value(
AtomComponentProperties.camera('Field of view')) == camera_fov_value) AtomComponentProperties.camera('Field of view')) == camera_fov_value)
# 21. Enter game mode. # 21. Enter/Exit game mode taking screenshot.
TestHelper.enter_game_mode(Tests.enter_game_mode) enter_exit_game_mode_take_screenshot(f"{SCREENSHOT_NAME}.ppm", Tests.enter_game_mode, Tests.exit_game_mode)
TestHelper.wait_for_condition(function=lambda: general.is_in_game_mode(), timeout_in_seconds=4.0)
# 22. Take screenshot. # 22. Look for errors.
ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{SCREENSHOT_NAME}.ppm")
# 23. Exit game mode.
TestHelper.exit_game_mode(Tests.exit_game_mode)
TestHelper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=4.0)
# 24. Look for errors.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors: for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")

@ -360,18 +360,6 @@ void ReflectedPropertyControl::CreateItems(XmlNodeRef node, CVarBlockPtr& outBlo
textureVar->Set(textureName); textureVar->Set(textureName);
} }
} }
else if (!azstricmp(type, "color"))
{
CSmartVariable<Vec3> colorVar;
AddVariable(group, colorVar, child->getTag(), humanReadableName.toUtf8().data(), strDescription.toUtf8().data(), func, pUserData, IVariable::DT_COLOR);
ColorB color;
if (child->getAttr("value", color))
{
ColorF colorLinear = ColorGammaToLinear(QColor(color.r, color.g, color.b));
Vec3 colorVec3(colorLinear.r, colorLinear.g, colorLinear.b);
colorVar->Set(colorVec3);
}
}
} }
} }

@ -243,9 +243,6 @@ void ReflectedPropertyItem::SetVariable(IVariable *var)
case ePropertySelection: case ePropertySelection:
m_reflectedVarAdapter = new ReflectedVarEnumAdapter; m_reflectedVarAdapter = new ReflectedVarEnumAdapter;
break; break;
case ePropertyColor:
m_reflectedVarAdapter = new ReflectedVarColorAdapter;
break;
case ePropertyUser: case ePropertyUser:
m_reflectedVarAdapter = new ReflectedVarUserAdapter; m_reflectedVarAdapter = new ReflectedVarUserAdapter;
break; break;

@ -312,50 +312,6 @@ void ReflectedVarVector4Adapter::SyncIVarToReflectedVar(IVariable *pVariable)
pVariable->Set(Vec4(m_reflectedVar->m_value.GetX(), m_reflectedVar->m_value.GetY(), m_reflectedVar->m_value.GetZ(), m_reflectedVar->m_value.GetW())); pVariable->Set(Vec4(m_reflectedVar->m_value.GetX(), m_reflectedVar->m_value.GetY(), m_reflectedVar->m_value.GetZ(), m_reflectedVar->m_value.GetW()));
} }
void ReflectedVarColorAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarColor(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
}
void ReflectedVarColorAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
if (pVariable->GetType() == IVariable::VECTOR)
{
Vec3 v(0, 0, 0);
pVariable->Get(v);
const QColor col = ColorLinearToGamma(ColorF(v.x, v.y, v.z));
m_reflectedVar->m_color.Set(static_cast<float>(col.redF()), static_cast<float>(col.greenF()), static_cast<float>(col.blueF()));
}
else
{
int col(0);
pVariable->Get(col);
const QColor qcolor = ColorToQColor((uint32)col);
m_reflectedVar->m_color.Set(static_cast<float>(qcolor.redF()), static_cast<float>(qcolor.greenF()), static_cast<float>(qcolor.blueF()));
}
}
void ReflectedVarColorAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
if (pVariable->GetType() == IVariable::VECTOR)
{
ColorF colLin = ColorGammaToLinear(QColor::fromRgbF(m_reflectedVar->m_color.GetX(), m_reflectedVar->m_color.GetY(), m_reflectedVar->m_color.GetZ()));
pVariable->Set(Vec3(colLin.r, colLin.g, colLin.b));
}
else
{
int ir = static_cast<int>(m_reflectedVar->m_color.GetX() * 255.0f);
int ig = static_cast<int>(m_reflectedVar->m_color.GetY() * 255.0f);
int ib = static_cast<int>(m_reflectedVar->m_color.GetZ() * 255.0f);
pVariable->Set(static_cast<int>(RGB(ir, ig, ib)));
}
}
void ReflectedVarResourceAdapter::SetVariable(IVariable *pVariable) void ReflectedVarResourceAdapter::SetVariable(IVariable *pVariable)
{ {
m_reflectedVar.reset(new CReflectedVarResource(pVariable->GetHumanName().toUtf8().data())); m_reflectedVar.reset(new CReflectedVarResource(pVariable->GetHumanName().toUtf8().data()));

@ -186,21 +186,6 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
}; };
class EDITOR_CORE_API ReflectedVarColorAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarColor > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarResourceAdapter class EDITOR_CORE_API ReflectedVarResourceAdapter
: public ReflectedVarAdapter : public ReflectedVarAdapter
{ {

@ -1,46 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Qt
#include <QColor>
#include <CryCommon/Cry_Color.h>
//////////////////////////////////////////////////////////////////////////
QColor ColorLinearToGamma(ColorF col)
{
float r = clamp_tpl(col.r, 0.0f, 1.0f);
float g = clamp_tpl(col.g, 0.0f, 1.0f);
float b = clamp_tpl(col.b, 0.0f, 1.0f);
float a = clamp_tpl(col.a, 0.0f, 1.0f);
r = (float)(r <= 0.0031308 ? (12.92 * r) : (1.055 * pow((double)r, 1.0 / 2.4) - 0.055));
g = (float)(g <= 0.0031308 ? (12.92 * g) : (1.055 * pow((double)g, 1.0 / 2.4) - 0.055));
b = (float)(b <= 0.0031308 ? (12.92 * b) : (1.055 * pow((double)b, 1.0 / 2.4) - 0.055));
return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f), int(a * 255.0f));
}
//////////////////////////////////////////////////////////////////////////
ColorF ColorGammaToLinear(const QColor& col)
{
float r = (float)col.red() / 255.0f;
float g = (float)col.green() / 255.0f;
float b = (float)col.blue() / 255.0f;
float a = (float)col.alpha() / 255.0f;
return ColorF((float)(r <= 0.04045 ? (r / 12.92) : pow(((double)r + 0.055) / 1.055, 2.4)),
(float)(g <= 0.04045 ? (g / 12.92) : pow(((double)g + 0.055) / 1.055, 2.4)),
(float)(b <= 0.04045 ? (b / 12.92) : pow(((double)b + 0.055) / 1.055, 2.4)), a);
}
QColor ColorToQColor(uint32 color)
{
return QColor::fromRgbF((float)GetRValue(color) / 255.0f,
(float)GetGValue(color) / 255.0f,
(float)GetBValue(color) / 255.0f);
}

@ -6,6 +6,7 @@
* *
*/ */
#include <AzCore/Math/Color.h>
#include "EditorDefs.h" #include "EditorDefs.h"
@ -184,9 +185,9 @@ QColor ColorLinearToGamma(ColorF col)
float b = clamp_tpl(col.b, 0.0f, 1.0f); float b = clamp_tpl(col.b, 0.0f, 1.0f);
float a = clamp_tpl(col.a, 0.0f, 1.0f); float a = clamp_tpl(col.a, 0.0f, 1.0f);
r = (float)(r <= 0.0031308 ? (12.92 * r) : (1.055 * pow((double)r, 1.0 / 2.4) - 0.055)); r = AZ::Color::ConvertSrgbLinearToGamma(r);
g = (float)(g <= 0.0031308 ? (12.92 * g) : (1.055 * pow((double)g, 1.0 / 2.4) - 0.055)); g = AZ::Color::ConvertSrgbLinearToGamma(g);
b = (float)(b <= 0.0031308 ? (12.92 * b) : (1.055 * pow((double)b, 1.0 / 2.4) - 0.055)); b = AZ::Color::ConvertSrgbLinearToGamma(b);
return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f), int(a * 255.0f)); return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f), int(a * 255.0f));
} }
@ -199,9 +200,9 @@ ColorF ColorGammaToLinear(const QColor& col)
float b = (float)col.blue() / 255.0f; float b = (float)col.blue() / 255.0f;
float a = (float)col.alpha() / 255.0f; float a = (float)col.alpha() / 255.0f;
return ColorF((float)(r <= 0.04045 ? (r / 12.92) : pow(((double)r + 0.055) / 1.055, 2.4)), return ColorF(AZ::Color::ConvertSrgbGammaToLinear(r),
(float)(g <= 0.04045 ? (g / 12.92) : pow(((double)g + 0.055) / 1.055, 2.4)), AZ::Color::ConvertSrgbGammaToLinear(g),
(float)(b <= 0.04045 ? (b / 12.92) : pow(((double)b + 0.055) / 1.055, 2.4)), a); AZ::Color::ConvertSrgbGammaToLinear(b), a);
} }
QColor ColorToQColor(uint32 color) QColor ColorToQColor(uint32 color)

@ -48,7 +48,6 @@ set(FILES
Util/Image.cpp Util/Image.cpp
Util/ImageHistogram.h Util/ImageHistogram.h
Util/Image.h Util/Image.h
Util/ColorUtils.cpp
Undo/Undo.cpp Undo/Undo.cpp
Undo/IUndoManagerListener.h Undo/IUndoManagerListener.h
Undo/IUndoObject.h Undo/IUndoObject.h

@ -134,6 +134,12 @@ namespace AZ
//! Color from u32 => 0xAABBGGRR, RGB convert from Gamma corrected to Linear values. //! Color from u32 => 0xAABBGGRR, RGB convert from Gamma corrected to Linear values.
void FromU32GammaToLinear(u32 c); void FromU32GammaToLinear(u32 c);
//! Convert SRGB gamma space to linear space
static float ConvertSrgbGammaToLinear(float x);
//! Convert SRGB linear space to gamma space
static float ConvertSrgbLinearToGamma(float x);
//! Convert color from linear to gamma corrected space. //! Convert color from linear to gamma corrected space.
Color LinearToGamma() const; Color LinearToGamma() const;

@ -370,6 +370,15 @@ namespace AZ
*this = GammaToLinear(); *this = GammaToLinear();
} }
AZ_MATH_INLINE float Color::ConvertSrgbGammaToLinear(float x)
{
return x <= 0.04045 ? (x / 12.92f) : static_cast<float>(pow((static_cast<double>(x) + 0.055) / 1.055, 2.4));
}
AZ_MATH_INLINE float Color::ConvertSrgbLinearToGamma(float x)
{
return x <= 0.0031308 ? 12.92f * x : static_cast<float>(1.055 * pow(static_cast<double>(x), 1.0 / 2.4) - 0.055);
}
AZ_MATH_INLINE Color Color::LinearToGamma() const AZ_MATH_INLINE Color Color::LinearToGamma() const
{ {
@ -377,9 +386,9 @@ namespace AZ
float g = GetG(); float g = GetG();
float b = GetB(); float b = GetB();
r = (r <= 0.0031308 ? 12.92f * r : static_cast<float>(1.055 * pow(static_cast<double>(r), 1.0 / 2.4) - 0.055)); r = ConvertSrgbLinearToGamma(r);
g = (g <= 0.0031308 ? 12.92f * g : static_cast<float>(1.055 * pow(static_cast<double>(g), 1.0 / 2.4) - 0.055)); g = ConvertSrgbLinearToGamma(g);
b = (b <= 0.0031308 ? 12.92f * b : static_cast<float>(1.055 * pow(static_cast<double>(b), 1.0 / 2.4) - 0.055)); b = ConvertSrgbLinearToGamma(b);
return Color(r,g,b,GetA()); return Color(r,g,b,GetA());
} }
@ -391,9 +400,9 @@ namespace AZ
float g = GetG(); float g = GetG();
float b = GetB(); float b = GetB();
return Color(r <= 0.04045 ? (r / 12.92f) : static_cast<float>(pow((static_cast<double>(r) + 0.055) / 1.055, 2.4)), return Color(ConvertSrgbGammaToLinear(r),
g <= 0.04045 ? (g / 12.92f) : static_cast<float>(pow((static_cast<double>(g) + 0.055) / 1.055, 2.4)), ConvertSrgbGammaToLinear(g),
b <= 0.04045 ? (b / 12.92f) : static_cast<float>(pow((static_cast<double>(b) + 0.055) / 1.055, 2.4)), GetA()); ConvertSrgbGammaToLinear(b), GetA());
} }

@ -22,6 +22,7 @@
#include <AzCore/Serialization/Json/UnorderedSetSerializer.h> #include <AzCore/Serialization/Json/UnorderedSetSerializer.h>
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h> #include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Settings/ConfigurableStack.h>
#include <AzCore/std/any.h> #include <AzCore/std/any.h>
#include <AzCore/std/optional.h> #include <AzCore/std/optional.h>
#include <AzCore/std/tuple.h> #include <AzCore/std/tuple.h>
@ -50,6 +51,8 @@ namespace AZ
void JsonSystemComponent::Reflect(ReflectContext* reflectContext) void JsonSystemComponent::Reflect(ReflectContext* reflectContext)
{ {
JsonConfigurableStackSerializer::Reflect(reflectContext);
if (JsonRegistrationContext* jsonContext = azrtti_cast<JsonRegistrationContext*>(reflectContext)) if (JsonRegistrationContext* jsonContext = azrtti_cast<JsonRegistrationContext*>(reflectContext))
{ {
jsonContext->Serializer<JsonBoolSerializer>()->HandlesType<bool>(); jsonContext->Serializer<JsonBoolSerializer>()->HandlesType<bool>();

@ -8,11 +8,12 @@
#pragma once #pragma once
#include <AzCore/Math/Uuid.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/ReflectContext.h> #include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h> #include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/std/containers/unordered_map.h> #include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/smart_ptr/unique_ptr.h> #include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AZ namespace AZ
@ -22,6 +23,7 @@ namespace AZ
{ {
public: public:
AZ_RTTI(JsonRegistrationContext, "{5A763774-CA8B-4245-A897-A03C503DCD60}", ReflectContext); AZ_RTTI(JsonRegistrationContext, "{5A763774-CA8B-4245-A897-A03C503DCD60}", ReflectContext);
AZ_CLASS_ALLOCATOR(JsonRegistrationContext, SystemAllocator, 0);
class SerializerBuilder; class SerializerBuilder;
using SerializerMap = AZStd::unordered_map<Uuid, AZStd::unique_ptr<BaseJsonSerializer>, AZStd::hash<Uuid>>; using SerializerMap = AZStd::unordered_map<Uuid, AZStd::unique_ptr<BaseJsonSerializer>, AZStd::hash<Uuid>>;

@ -0,0 +1,172 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Settings/ConfigurableStack.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/std/containers/queue.h>
#include <AzCore/std/tuple.h>
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(JsonConfigurableStackSerializer, AZ::SystemAllocator, 0);
JsonSerializationResult::Result JsonConfigurableStackSerializer::Load(
void* outputValue,
[[maybe_unused]] const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadFromArray(outputValue, inputValue, context);
case rapidjson::kObjectType:
return LoadFromObject(outputValue, inputValue, context);
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
[[fallthrough]];
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
return context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Configurable stack values can only be read from arrays or objects.");
default:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for string value.");
}
}
JsonSerializationResult::Result JsonConfigurableStackSerializer::Store(
[[maybe_unused]] rapidjson::Value& outputValue,
[[maybe_unused]] const void* inputValue,
[[maybe_unused]] const void* defaultValue,
[[maybe_unused]] const Uuid& valueTypeId,
JsonSerializerContext& context)
{
return context.Report(
JsonSerializationResult::Tasks::WriteValue, JsonSerializationResult::Outcomes::Unsupported,
"Configuration stacks can not be written out.");
}
void JsonConfigurableStackSerializer::Reflect(ReflectContext* context)
{
if (JsonRegistrationContext* jsonContext = azrtti_cast<JsonRegistrationContext*>(context))
{
jsonContext->Serializer<JsonConfigurableStackSerializer>()->HandlesType<ConfigurableStack>();
}
}
JsonSerializationResult::Result JsonConfigurableStackSerializer::LoadFromArray(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
auto stack = reinterpret_cast<ConfigurableStackInterface*>(outputValue);
const Uuid& nodeValueType = stack->GetNodeType();
JSR::ResultCode result(JSR::Tasks::ReadField);
uint32_t counter = 0;
for (auto& it : inputValue.GetArray())
{
ScopedContextPath subPath(context, counter);
void* value = stack->AddNode(AZStd::to_string(counter));
result.Combine(ContinueLoading(value, nodeValueType, it, context));
counter++;
}
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Loaded configurable stack from array."
: "Failed to load configurable stack from array.");
}
JsonSerializationResult::Result JsonConfigurableStackSerializer::LoadFromObject(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
auto stack = reinterpret_cast<ConfigurableStackInterface*>(outputValue);
const Uuid& nodeValueType = stack->GetNodeType();
AZStd::queue<AZStd::tuple<ConfigurableStackInterface::InsertPosition, AZStd::string_view, rapidjson::Value::ConstMemberIterator>>
delayedEntries;
JSR::ResultCode result(JSR::Tasks::ReadField);
auto add = [&](ConfigurableStackInterface::InsertPosition position, rapidjson::Value::ConstMemberIterator target,
rapidjson::Value::ConstMemberIterator it)
{
if (target->value.IsString())
{
delayedEntries.emplace(position, AZStd::string_view(target->value.GetString(), target->value.GetStringLength()), it);
}
else
{
result.Combine(context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Skipped,
"Skipped value for the Configurable Stack because the target wasn't a string."));
}
};
// Load all the regular entries into the stack and store any with a before or after binding for
// later inserting.
for (auto it = inputValue.MemberBegin(); it != inputValue.MemberEnd(); ++it)
{
AZStd::string_view name(it->name.GetString(), it->name.GetStringLength());
ScopedContextPath subPath(context, name);
if (it->value.IsObject())
{
if (auto target = it->value.FindMember(StackBefore); target != it->value.MemberEnd())
{
add(ConfigurableStackInterface::InsertPosition::Before, target, it);
continue;
}
if (auto target = it->value.FindMember(StackAfter); target != it->value.MemberEnd())
{
add(ConfigurableStackInterface::InsertPosition::After, target, it);
continue;
}
}
void* value = stack->AddNode(name);
result.Combine(ContinueLoading(value, nodeValueType, it->value, context));
}
// Insert the entries that have been delayed.
while (!delayedEntries.empty())
{
auto&& [insertPosition, target, valueStore] = delayedEntries.front();
AZStd::string_view name(valueStore->name.GetString(), valueStore->name.GetStringLength());
ScopedContextPath subPath(context, name);
void* value = stack->AddNode(name, target, insertPosition);
if (value != nullptr)
{
result.Combine(ContinueLoading(value, nodeValueType, valueStore->value, context));
}
else
{
result.Combine(context.Report(
JSR::Tasks::ReadField, JSR::Outcomes::Skipped,
AZStd::string::format(
"Skipped value for the Configurable Stack because the target '%.*s' couldn't be found.", AZ_STRING_ARG(name))));
}
delayedEntries.pop();
}
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Loaded configurable stack from array."
: "Failed to load configurable stack from array.");
}
} // namespace AZ

@ -0,0 +1,187 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/base.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/utils.h>
namespace AZ
{
//! The ConfigurableStack makes configuring stacks and arrays through the Settings Registry easier than using direct serialization
//! to a container like AZStd::vector. It does this by using JSON Objects rather than JSON arrays, although arrays are supported
//! for backwards compatibility. Two key words were added:
//! - $stack_before : Insert the new entry before the referenced entry. Referencing is done by name.
//! - $stack_after : Insert the new entry after the referenced entry. Referencing is done by name.
//! to allow inserting new entries at specific locations. An example of a .setreg file at updates existing settings would be:
//! // Original settings
//! {
//! "Settings in a stack":
//! {
//! "AnOriginalEntry":
//! {
//! "MyValue": "hello",
//! "ExampleValue": 84
//! },
//! "TheSecondEntry":
//! {
//! "MyValue": "world"
//! }
//! }
//! }
//!
//! // Customized settings.
//! {
//! "Settings in a stack":
//! {
//! // Add a new entry before "AnOriginalEntry" in the original document.
//! "NewEntry":
//! {
//! "$stack_before": "AnOriginalEntry",
//! "MyValue": 42
//! },
//! // Add a second entry after "AnOriginalEntry" in the original document.
//! "SecondNewEntry":
//! {
//! "$stack_after": "AnOriginalEntry",
//! "MyValue": "FortyTwo".
//! },
//! // Update a value in "AnOriginalEntry".
//! "AnOriginalEntry":
//! {
//! "ExampleValue": 42
//! },
//! // Delete the "TheSecondEntry" from the settings.
//! "TheSecondEntry" : null,
//! }
//! }
//!
//! The ConfigurableStack uses an AZStd::shared_ptr to store the values. This supports settings up a base class and specifying
//! derived classes in the settings, but requires that the base and derived classes all have a memory allocator associated with
//! them (i.e. by using the "AZ_CLASS_ALLOCATOR" macro) and that the relation of the classes is reflected. Loading a
//! ConfigurableStack can be done using the GetObject call on the SettingsRegistryInterface.
class ReflectContext;
class ConfigurableStackInterface
{
public:
friend class JsonConfigurableStackSerializer;
virtual ~ConfigurableStackInterface() = default;
virtual const TypeId& GetNodeType() const = 0;
protected:
enum class InsertPosition
{
Before,
After
};
virtual void* AddNode(AZStd::string name) = 0;
virtual void* AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position) = 0;
};
template<typename StackBaseType>
class ConfigurableStack final : public ConfigurableStackInterface
{
public:
using NodeValue = AZStd::shared_ptr<StackBaseType>;
using Node = AZStd::pair<AZStd::string, NodeValue>;
using NodeContainer = AZStd::vector<Node>;
using Iterator = typename NodeContainer::iterator;
using ConstIterator = typename NodeContainer::const_iterator;
~ConfigurableStack() override = default;
const TypeId& GetNodeType() const override;
Iterator begin();
Iterator end();
ConstIterator begin() const;
ConstIterator end() const;
size_t size() const;
protected:
void* AddNode(AZStd::string name) override;
void* AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position) override;
private:
NodeContainer m_nodes;
};
AZ_TYPE_INFO_TEMPLATE(ConfigurableStack, "{0A3D2038-6E6A-4EFD-A1B4-F30D947E21B1}", AZ_TYPE_INFO_TYPENAME);
template<typename StackBaseType>
struct SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>
{
using ConfigurableStackType = ConfigurableStack<StackBaseType>;
class GenericConfigurableStackInfo : public GenericClassInfo
{
public:
AZ_TYPE_INFO(GenericConfigurableStackInfo, "{FC5A9353-D0DE-48F6-81B5-1CB2985C5F65}");
GenericConfigurableStackInfo();
SerializeContext::ClassData* GetClassData() override;
size_t GetNumTemplatedArguments() override;
const Uuid& GetTemplatedTypeId([[maybe_unused]] size_t element) override;
const Uuid& GetSpecializedTypeId() const override;
const Uuid& GetGenericTypeId() const override;
void Reflect(SerializeContext* serializeContext) override;
SerializeContext::ClassData m_classData;
};
using ClassInfoType = GenericConfigurableStackInfo;
static ClassInfoType* GetGenericInfo();
static const Uuid& GetClassTypeId();
};
class JsonConfigurableStackSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonConfigurableStackSerializer, "{45A31805-9058-41A9-B1A3-71E2CB4D9237}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(
rapidjson::Value& outputValue,
const void* inputValue,
const void* defaultValue,
const Uuid& valueTypeId,
JsonSerializerContext& context) override;
static void Reflect(ReflectContext* context);
private:
JsonSerializationResult::Result LoadFromArray(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context);
JsonSerializationResult::Result LoadFromObject(
void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context);
static constexpr const char* StackBefore = "$stack_before";
static constexpr const char* StackAfter = "$stack_after";
};
} // namespace AZ
#include <AzCore/Settings/ConfigurableStack.inl>

@ -0,0 +1,149 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace AZ
{
//
// ConfigurableStack
//
template<typename StackBaseType>
const TypeId& ConfigurableStack<StackBaseType>::GetNodeType() const
{
return azrtti_typeid<NodeValue>();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::begin() -> Iterator
{
return m_nodes.begin();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::end() -> Iterator
{
return m_nodes.end();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::begin() const -> ConstIterator
{
return m_nodes.begin();
}
template<typename StackBaseType>
auto ConfigurableStack<StackBaseType>::end() const -> ConstIterator
{
return m_nodes.end();
}
template<typename StackBaseType>
size_t ConfigurableStack<StackBaseType>::size() const
{
return m_nodes.size();
}
template<typename StackBaseType>
void* ConfigurableStack<StackBaseType>::AddNode(AZStd::string name)
{
Node& result = m_nodes.emplace_back(AZStd::move(name));
return &result.second;
}
template<typename StackBaseType>
void* ConfigurableStack<StackBaseType>::AddNode(AZStd::string name, AZStd::string_view target, InsertPosition position)
{
auto end = m_nodes.end();
for (auto it = m_nodes.begin(); it != end; ++it)
{
if (it->first == target)
{
if (position == InsertPosition::After)
{
++it;
}
auto result = m_nodes.insert(it, Node(AZStd::move(name), {}));
return &result->second;
}
}
return nullptr;
}
//
// SerializeGenericTypeInfo
//
template<typename StackBaseType>
SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GenericConfigurableStackInfo()
: m_classData{ SerializeContext::ClassData::Create<ConfigurableStackType>(
"AZ::ConfigurableStack", GetSpecializedTypeId(), Internal::NullFactory::GetInstance(), nullptr, nullptr) }
{
}
template<typename StackBaseType>
SerializeContext::ClassData* SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetClassData()
{
return &m_classData;
}
template<typename StackBaseType>
size_t SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetNumTemplatedArguments()
{
return 1;
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetTemplatedTypeId(
[[maybe_unused]] size_t element)
{
return SerializeGenericTypeInfo<StackBaseType>::GetClassTypeId();
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetSpecializedTypeId() const
{
return azrtti_typeid<ConfigurableStackType>();
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::GetGenericTypeId() const
{
return TYPEINFO_Uuid();
}
template<typename StackBaseType>
void SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GenericConfigurableStackInfo::Reflect(SerializeContext* serializeContext)
{
if (serializeContext)
{
serializeContext->RegisterGenericClassInfo(GetSpecializedTypeId(), this, &AnyTypeInfoConcept<ConfigurableStackType>::CreateAny);
if (serializeContext->FindClassData(azrtti_typeid<AZStd::shared_ptr<StackBaseType>>()) == nullptr)
{
serializeContext->RegisterGenericType<AZStd::shared_ptr<StackBaseType>>();
}
}
}
template<typename StackBaseType>
auto SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GetGenericInfo() -> ClassInfoType*
{
return GetCurrentSerializeContextModule().CreateGenericClassInfo<ConfigurableStackType>();
}
template<typename StackBaseType>
const Uuid& SerializeGenericTypeInfo<ConfigurableStack<StackBaseType>>::GetClassTypeId()
{
return GetGenericInfo()->GetClassData()->m_typeId;
}
} // namespace AZ

@ -560,6 +560,9 @@ set(FILES
Serialization/std/VariantReflection.inl Serialization/std/VariantReflection.inl
Settings/CommandLine.cpp Settings/CommandLine.cpp
Settings/CommandLine.h Settings/CommandLine.h
Settings/ConfigurableStack.cpp
Settings/ConfigurableStack.inl
Settings/ConfigurableStack.h
Settings/SettingsRegistry.cpp Settings/SettingsRegistry.cpp
Settings/SettingsRegistry.h Settings/SettingsRegistry.h
Settings/SettingsRegistryConsoleUtils.cpp Settings/SettingsRegistryConsoleUtils.cpp

@ -0,0 +1,281 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/JSON/document.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Settings/ConfigurableStack.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
struct ConfigInt
{
AZ_TYPE_INFO(UnitTest::ConfigInt, "{1FAF6E55-7FA4-4FFA-8C41-34F422B8E8AB}");
AZ_CLASS_ALLOCATOR(ConfigInt, AZ::SystemAllocator, 0);
int m_value;
static void Reflect(AZ::ReflectContext* context)
{
if (auto sc = azrtti_cast<AZ::SerializeContext*>(context))
{
sc->Class<ConfigInt>()->Field("Value", &ConfigInt::m_value);
}
}
};
struct ConfigurableStackTests : public ScopedAllocatorSetupFixture
{
void Reflect(AZ::ReflectContext* context)
{
if (auto sc = azrtti_cast<AZ::SerializeContext*>(context))
{
AZ::JsonSystemComponent::Reflect(sc);
ConfigInt::Reflect(sc);
sc->RegisterGenericType<AZ::ConfigurableStack<ConfigInt>>();
}
else if (auto jrc = azrtti_cast<AZ::JsonRegistrationContext*>(context))
{
AZ::JsonSystemComponent::Reflect(jrc);
}
}
void SetUp() override
{
ScopedAllocatorSetupFixture::SetUp();
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
m_jsonRegistrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
Reflect(m_serializeContext.get());
Reflect(m_jsonRegistrationContext.get());
m_deserializationSettings.m_registrationContext = m_jsonRegistrationContext.get();
m_deserializationSettings.m_serializeContext = m_serializeContext.get();
}
void TearDown()
{
m_jsonRegistrationContext->EnableRemoveReflection();
Reflect(m_jsonRegistrationContext.get());
m_jsonRegistrationContext->DisableRemoveReflection();
m_serializeContext->EnableRemoveReflection();
Reflect(m_serializeContext.get());
m_serializeContext->DisableRemoveReflection();
ScopedAllocatorSetupFixture::TearDown();
}
void ObjectTest(AZStd::string_view jsonText)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(jsonText.data(), jsonText.length());
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
ASSERT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
ASSERT_EQ(4, stack.size());
int numberCounter = 0;
int valueCounter = 42;
for (auto& [name, value] : stack)
{
EXPECT_STREQ(AZStd::string::format("Value%i", numberCounter).c_str(), name.c_str());
EXPECT_EQ(valueCounter, value->m_value);
numberCounter++;
valueCounter++;
}
}
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
AZStd::unique_ptr<AZ::JsonRegistrationContext> m_jsonRegistrationContext;
AZ::JsonDeserializerSettings m_deserializationSettings;
};
TEST_F(ConfigurableStackTests, DeserializeArray)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(
R"([
{ "Value": 42 },
{ "Value": 43 },
{ "Value": 44 },
{ "Value": 45 }
])");
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
ASSERT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
ASSERT_EQ(4, stack.size());
int numberCounter = 0;
int valueCounter = 42;
for (auto& [name, value] : stack)
{
EXPECT_STREQ(AZStd::to_string(numberCounter).c_str(), name.c_str());
EXPECT_EQ(valueCounter, value->m_value);
numberCounter++;
valueCounter++;
}
}
TEST_F(ConfigurableStackTests, DeserializeObject)
{
ObjectTest(
R"({
"Value0": { "Value": 42 },
"Value1": { "Value": 43 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithLateBefore)
{
ObjectTest(
R"({
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 },
"Value1":
{
"$stack_before": "Value2",
"Value": 43
}
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithEarlyBefore)
{
ObjectTest(
R"({
"Value1":
{
"$stack_before": "Value2",
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithLateAfter)
{
ObjectTest(
R"({
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 },
"Value1":
{
"$stack_after": "Value0",
"Value": 43
}
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithEarlyAfter)
{
ObjectTest(
R"({
"Value1":
{
"$stack_after": "Value0",
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithBeforeFirst)
{
ObjectTest(
R"({
"Value1": { "Value": 43 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 },
"Value0":
{
"$stack_before": "Value1",
"Value": 42
}
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithInsertAfterLast)
{
ObjectTest(
R"({
"Value3":
{
"$stack_after": "Value2",
"Value": 45
},
"Value0": { "Value": 42 },
"Value1": { "Value": 43 },
"Value2": { "Value": 44 }
})");
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithInvalidTarget)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(
R"({
"Value1":
{
"$stack_after": "airplane",
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialSkip, result.GetOutcome());
EXPECT_EQ(3, stack.size());
}
TEST_F(ConfigurableStackTests, DeserializeObjectWithInvalidTargetType)
{
AZ::ConfigurableStack<ConfigInt> stack;
rapidjson::Document document;
document.Parse(
R"({
"Value1":
{
"$stack_after": 42,
"Value": 43
},
"Value0": { "Value": 42 },
"Value2": { "Value": 44 },
"Value3": { "Value": 45 }
})");
ASSERT_FALSE(document.HasParseError());
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(stack, document, m_deserializationSettings);
EXPECT_EQ(AZ::JsonSerializationResult::Processing::Completed, result.GetProcessing());
EXPECT_EQ(AZ::JsonSerializationResult::Outcomes::PartialSkip, result.GetOutcome());
EXPECT_EQ(3, stack.size());
}
} // namespace UnitTest

@ -76,6 +76,7 @@ set(FILES
Name/NameTests.cpp Name/NameTests.cpp
RTTI/TypeSafeIntegralTests.cpp RTTI/TypeSafeIntegralTests.cpp
Settings/CommandLineTests.cpp Settings/CommandLineTests.cpp
Settings/ConfigurableStackTests.cpp
Settings/SettingsRegistryTests.cpp Settings/SettingsRegistryTests.cpp
Settings/SettingsRegistryConsoleUtilsTests.cpp Settings/SettingsRegistryConsoleUtilsTests.cpp
Settings/SettingsRegistryMergeUtilsTests.cpp Settings/SettingsRegistryMergeUtilsTests.cpp

@ -140,28 +140,28 @@ namespace AzFramework
//! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each //! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each
//! XY coordinate in the list. //! XY coordinate in the list.
virtual void ProcessHeightsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessHeightsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessNormalsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessNormalsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfaceWeightsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessSurfaceWeightsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfacePointsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessSurfacePointsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessHeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessHeightsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessNormalsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessNormalsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const = 0; Sampler sampleFilter = Sampler::DEFAULT) const = 0;
@ -272,42 +272,42 @@ namespace AzFramework
//! Asynchronous versions of the various 'Process*' API functions declared above. //! Asynchronous versions of the various 'Process*' API functions declared above.
//! It's the responsibility of the caller to ensure all callbacks are threadsafe. //! It's the responsibility of the caller to ensure all callbacks are threadsafe.
virtual AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListAsync( virtual AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListAsync( virtual AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListAsync( virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListAsync( virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListOfVector2Async( virtual AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListOfVector2Async( virtual AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListOfVector2Async( virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;
virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListOfVector2Async( virtual AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
SurfacePointListFillCallback perPositionCallback, SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const = 0;

@ -77,21 +77,21 @@ namespace UnitTest
MOCK_CONST_METHOD5( MOCK_CONST_METHOD5(
GetSurfacePointFromFloats, void(float, float, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*)); GetSurfacePointFromFloats, void(float, float, AzFramework::SurfaceData::SurfacePoint&, Sampler, bool*));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessHeightsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessHeightsFromList, void(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessNormalsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessNormalsFromList, void(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessSurfaceWeightsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessSurfaceWeightsFromList, void(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessSurfacePointsFromList, void(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessSurfacePointsFromList, void(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessHeightsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessHeightsFromListOfVector2, void(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessNormalsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessNormalsFromListOfVector2, void(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessSurfaceWeightsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessSurfaceWeightsFromListOfVector2, void(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD3( MOCK_CONST_METHOD3(
ProcessSurfacePointsFromListOfVector2, void(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler)); ProcessSurfacePointsFromListOfVector2, void(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler));
MOCK_CONST_METHOD2( MOCK_CONST_METHOD2(
GetNumSamplesFromRegion, AZStd::pair<size_t, size_t>(const AZ::Aabb&, const AZ::Vector2&)); GetNumSamplesFromRegion, AZStd::pair<size_t, size_t>(const AZ::Aabb&, const AZ::Vector2&));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
@ -107,21 +107,21 @@ namespace UnitTest
MOCK_CONST_METHOD1( MOCK_CONST_METHOD1(
GetClosestIntersection, AzFramework::RenderGeometry::RayResult(const AzFramework::RenderGeometry::RayRequest&)); GetClosestIntersection, AzFramework::RenderGeometry::RayResult(const AzFramework::RenderGeometry::RayRequest&));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessHeightsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessHeightsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessNormalsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessNormalsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessSurfaceWeightsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessSurfaceWeightsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessSurfacePointsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessSurfacePointsFromListAsync, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector3>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessHeightsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessHeightsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessNormalsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessNormalsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessSurfaceWeightsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessSurfaceWeightsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD4( MOCK_CONST_METHOD4(
ProcessSurfacePointsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessSurfacePointsFromListOfVector2Async, AZStd::shared_ptr<TerrainJobContext>(const AZStd::span<const AZ::Vector2>&, AzFramework::Terrain::SurfacePointListFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD5( MOCK_CONST_METHOD5(
ProcessHeightsFromRegionAsync, AZStd::shared_ptr<TerrainJobContext>(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>)); ProcessHeightsFromRegionAsync, AZStd::shared_ptr<TerrainJobContext>(const AZ::Aabb&, const AZ::Vector2&, AzFramework::Terrain::SurfacePointRegionFillCallback, Sampler, AZStd::shared_ptr<ProcessAsyncParams>));
MOCK_CONST_METHOD5( MOCK_CONST_METHOD5(

@ -11,6 +11,7 @@
#include <Processing/PixelFormatInfo.h> #include <Processing/PixelFormatInfo.h>
#include <Processing/ImageFlags.h> #include <Processing/ImageFlags.h>
#include <Atom/ImageProcessing/PixelFormats.h> #include <Atom/ImageProcessing/PixelFormats.h>
#include <AzCore/Math/Color.h>
#include <Converters/FIR-Weights.h> #include <Converters/FIR-Weights.h>
#include <Converters/PixelOperation.h> #include <Converters/PixelOperation.h>
@ -118,19 +119,8 @@ namespace ImageProcessingAtom
float m_fMaxDiff = 0.0f; float m_fMaxDiff = 0.0f;
}; };
static FunctionLookupTable<1024> s_lutGammaToLinear(AZ::Color::ConvertSrgbGammaToLinear, 0.04045f, 0.00001f);
static float GammaToLinear(float x) static FunctionLookupTable<1024> s_lutLinearToGamma(AZ::Color::ConvertSrgbLinearToGamma, 0.05f, 0.00001f);
{
return (x <= 0.04045f) ? x / 12.92f : powf((x + 0.055f) / 1.055f, 2.4f);
}
static float LinearToGamma(float x)
{
return (x <= 0.0031308f) ? x * 12.92f : 1.055f * powf(x, 1.0f / 2.4f) - 0.055f;
}
static FunctionLookupTable<1024> s_lutGammaToLinear(GammaToLinear, 0.04045f, 0.00001f);
static FunctionLookupTable<1024> s_lutLinearToGamma(LinearToGamma, 0.05f, 0.00001f);
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////

@ -14,6 +14,8 @@
#include <Atom/RPI.Public/RPIUtils.h> #include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Shader/Shader.h> #include <Atom/RPI.Public/Shader/Shader.h>
#include <AzCore/Math/Color.h>
#include <AzCore/std/containers/array.h>
#include <AzFramework/Asset/AssetSystemBus.h> #include <AzFramework/Asset/AssetSystemBus.h>
namespace AZ namespace AZ
@ -105,6 +107,25 @@ namespace AZ
return ((value - origMin) / (origMax - origMin)) * (scaledMax - scaledMin) + scaledMin; return ((value - origMin) / (origMax - origMin)) * (scaledMax - scaledMin) + scaledMin;
} }
// Pre-compute a lookup table for converting SRGB gamma to linear
// by specifying the AZ::u8 so we don't have to do the computation
// when retrieving pixels
using ConversionLookupTable = AZStd::array<float, 256>;
ConversionLookupTable CreateSrgbGammaToLinearLookupTable()
{
ConversionLookupTable lookupTable;
for (size_t i = 0; i < lookupTable.array_size; ++i)
{
float srgbValue = i / static_cast<float>(std::numeric_limits<AZ::u8>::max());
lookupTable[i] = AZ::Color::ConvertSrgbGammaToLinear(srgbValue);
}
return lookupTable;
}
static ConversionLookupTable s_SrgbGammaToLinearLookupTable = CreateSrgbGammaToLinearLookupTable();
float RetrieveFloatValue(const AZ::u8* mem, size_t index, AZ::RHI::Format format) float RetrieveFloatValue(const AZ::u8* mem, size_t index, AZ::RHI::Format format)
{ {
switch (format) switch (format)
@ -113,12 +134,23 @@ namespace AZ
case AZ::RHI::Format::A8_UNORM: case AZ::RHI::Format::A8_UNORM:
case AZ::RHI::Format::R8G8_UNORM: case AZ::RHI::Format::R8G8_UNORM:
case AZ::RHI::Format::R8G8B8A8_UNORM: case AZ::RHI::Format::R8G8B8A8_UNORM:
case AZ::RHI::Format::A8B8G8R8_UNORM:
{ {
return mem[index] / static_cast<float>(std::numeric_limits<AZ::u8>::max()); return mem[index] / static_cast<float>(std::numeric_limits<AZ::u8>::max());
} }
case AZ::RHI::Format::R8_UNORM_SRGB:
case AZ::RHI::Format::R8G8_UNORM_SRGB:
case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB:
case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB:
{
// Use a lookup table that takes an AZ::u8 instead of a float
// for better performance
return s_SrgbGammaToLinearLookupTable[mem[index]];
}
case AZ::RHI::Format::R8_SNORM: case AZ::RHI::Format::R8_SNORM:
case AZ::RHI::Format::R8G8_SNORM: case AZ::RHI::Format::R8G8_SNORM:
case AZ::RHI::Format::R8G8B8A8_SNORM: case AZ::RHI::Format::R8G8B8A8_SNORM:
case AZ::RHI::Format::A8B8G8R8_SNORM:
{ {
// Scale the value from AZ::s8 min/max to -1 to 1 // Scale the value from AZ::s8 min/max to -1 to 1
// We need to treat -128 and -127 the same, so that we get a symmetric // We need to treat -128 and -127 the same, so that we get a symmetric
@ -452,9 +484,15 @@ namespace AZ
case AZ::RHI::Format::A8_UNORM: case AZ::RHI::Format::A8_UNORM:
case AZ::RHI::Format::R8G8_UNORM: case AZ::RHI::Format::R8G8_UNORM:
case AZ::RHI::Format::R8G8B8A8_UNORM: case AZ::RHI::Format::R8G8B8A8_UNORM:
case AZ::RHI::Format::A8B8G8R8_UNORM:
case AZ::RHI::Format::R8_UNORM_SRGB:
case AZ::RHI::Format::R8G8_UNORM_SRGB:
case AZ::RHI::Format::R8G8B8A8_UNORM_SRGB:
case AZ::RHI::Format::A8B8G8R8_UNORM_SRGB:
case AZ::RHI::Format::R8_SNORM: case AZ::RHI::Format::R8_SNORM:
case AZ::RHI::Format::R8G8_SNORM: case AZ::RHI::Format::R8G8_SNORM:
case AZ::RHI::Format::R8G8B8A8_SNORM: case AZ::RHI::Format::R8G8B8A8_SNORM:
case AZ::RHI::Format::A8B8G8R8_SNORM:
case AZ::RHI::Format::D16_UNORM: case AZ::RHI::Format::D16_UNORM:
case AZ::RHI::Format::R16_UNORM: case AZ::RHI::Format::R16_UNORM:
case AZ::RHI::Format::R16G16_UNORM: case AZ::RHI::Format::R16G16_UNORM:

@ -144,43 +144,45 @@ namespace SurfaceData
return false; return false;
} }
bool SurfaceDataMeshComponent::DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const void SurfaceDataMeshComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{ {
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex); GetSurfacePointsFromList(AZStd::span<const AZ::Vector3>(&inPosition, 1), surfacePointList);
}
// 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)) void SurfaceDataMeshComponent::GetSurfacePointsFromList(
AZStd::span<const AZ::Vector3> inPositions, SurfacePointList& surfacePointList) const
{ {
return false; AZ::Vector3 hitPosition;
} AZ::Vector3 hitNormal;
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
AZ::RPI::ModelAsset* mesh = m_meshAssetData.GetAs<AZ::RPI::ModelAsset>(); AZ::RPI::ModelAsset* mesh = m_meshAssetData.GetAs<AZ::RPI::ModelAsset>();
if (!mesh) if (!mesh)
{ {
return false; return;
} }
const AZ::Vector3 rayStart = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding); for (auto& inPosition : inPositions)
const AZ::Vector3 rayEnd = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMin().GetZ() - s_rayAABBHeightPadding);
return GetMeshRayIntersection(
*mesh, m_meshWorldTM, m_meshWorldTMInverse, m_meshNonUniformScale, rayStart, rayEnd, outPosition, outNormal);
}
void SurfaceDataMeshComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{ {
AZ::Vector3 hitPosition; // test AABB as first pass to claim the point
AZ::Vector3 hitNormal; if (SurfaceData::AabbContains2D(m_meshBounds, inPosition))
if (DoRayTrace(inPosition, hitPosition, hitNormal)) {
const AZ::Vector3 rayStart =
AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding);
const AZ::Vector3 rayEnd =
AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMin().GetZ() - s_rayAABBHeightPadding);
bool rayHit = GetMeshRayIntersection(
*mesh, m_meshWorldTM, m_meshWorldTMInverse, m_meshNonUniformScale, rayStart, rayEnd, hitPosition, hitNormal);
if (rayHit)
{ {
surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, hitPosition, hitNormal, m_newPointWeights); surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, hitPosition, hitNormal, m_newPointWeights);
} }
} }
}
}
AZ::Aabb SurfaceDataMeshComponent::GetSurfaceAabb() const AZ::Aabb SurfaceDataMeshComponent::GetSurfaceAabb() const
{ {

@ -80,9 +80,9 @@ namespace SurfaceData
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus // SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override; void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override;
void GetSurfacePointsFromList(AZStd::span<const AZ::Vector3> inPositions, SurfacePointList& surfacePointList) const override;
private: private:
bool DoRayTrace(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const;
void UpdateMeshData(); void UpdateMeshData();
void OnCompositionChanged(); void OnCompositionChanged();

@ -302,27 +302,30 @@ namespace EMStudio
} }
} }
// Check if motion event with this configuration exists and return color. // Check if motion event with this configuration exists and return color.
AZ::u32 MotionEventPresetManager::GetEventColor(const EMotionFX::EventDataSet& eventDatas) const AZ::u32 MotionEventPresetManager::GetEventColor(const EMotionFX::EventDataSet& eventDatas) const
{ {
for (const MotionEventPreset* preset : m_eventPresets) for (const MotionEventPreset* preset : m_eventPresets)
{ {
EMotionFX::EventDataSet commonDatas;
const EMotionFX::EventDataSet& presetDatas = preset->GetEventDatas(); const EMotionFX::EventDataSet& presetDatas = preset->GetEventDatas();
const bool allMatch = AZStd::all_of(presetDatas.cbegin(), presetDatas.cend(), [eventDatas](const EMotionFX::EventDataPtr& presetData)
const size_t numEventDatas = eventDatas.size();
if (numEventDatas == presetDatas.size())
{ {
const auto thisPresetDataHasMatch = AZStd::find_if(eventDatas.cbegin(), eventDatas.cend(), [presetData](const EMotionFX::EventDataPtr& eventData) for (size_t i = 0; i < numEventDatas; ++i)
{ {
return ((presetData && eventData && *presetData == *eventData) || (!presetData && !eventData)); const EMotionFX::EventDataPtr& eventData = eventDatas[i];
}); const EMotionFX::EventDataPtr& presetData = presetDatas[i];
return thisPresetDataHasMatch != eventDatas.cend();
}); if (eventData && presetData &&
if (allMatch) eventData->RTTI_GetType() == presetData->RTTI_GetType() &&
*eventData == *presetData)
{ {
return preset->GetEventColor(); return preset->GetEventColor();
} }
} }
}
}
// Use the same color for all events that are not from a preset. // Use the same color for all events that are not from a preset.
return s_unknownEventColor; return s_unknownEventColor;

@ -108,29 +108,9 @@ namespace GradientSignal
void AddTag(AZStd::string tag) override; void AddTag(AZStd::string tag) override;
private: private:
static float CalculateAltitudeRatio(const SurfaceData::SurfacePointList& points, size_t inPositionIndex, float altitudeMin, float altitudeMax)
{
if (points.IsEmpty(inPositionIndex))
{
return 0.0f;
}
// GetSurfacePoints (which was used to populate the points list) always returns points in decreasing height order, so the
// first point in the list contains the highest altitude.
const float highestAltitude = points.GetHighestSurfacePoint(inPositionIndex).m_position.GetZ();
// Turn the absolute altitude value into a 0-1 value by returning the % of the given altitude range that it falls at.
return GetRatio(altitudeMin, altitudeMax, highestAltitude);
}
mutable AZStd::shared_mutex m_cacheMutex; mutable AZStd::shared_mutex m_cacheMutex;
SurfaceAltitudeGradientConfig m_configuration; SurfaceAltitudeGradientConfig m_configuration;
LmbrCentral::DependencyMonitor m_dependencyMonitor; LmbrCentral::DependencyMonitor m_dependencyMonitor;
AZStd::atomic_bool m_dirty{ false }; AZStd::atomic_bool m_dirty{ false };
// m_surfacePointList exists here because it will more efficiently reuse memory across GetValue() calls if it stays constructed.
// The mutex exists to ensure that we don't try to use it from GetValue() in multiple threads at once.
mutable AZStd::recursive_mutex m_surfacePointListMutex;
mutable SurfaceData::SurfacePointList m_surfacePointList;
}; };
} }

@ -82,25 +82,6 @@ namespace GradientSignal
void AddTag(AZStd::string tag) override; void AddTag(AZStd::string tag) override;
private: private:
static float GetMaxSurfaceWeight(const SurfaceData::SurfacePointList& points)
{
float result = 0.0f;
points.EnumeratePoints([&result](
[[maybe_unused]] size_t inPositionIndex, [[maybe_unused]] const AZ::Vector3& position,
[[maybe_unused]] const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool
{
masks.EnumerateWeights(
[&result]([[maybe_unused]] AZ::Crc32 surfaceType, float weight) -> bool
{
result = AZ::GetMax(AZ::GetClamp(weight, 0.0f, 1.0f), result);
return true;
});
return true;
});
return result;
}
SurfaceMaskGradientConfig m_configuration; SurfaceMaskGradientConfig m_configuration;
LmbrCentral::DependencyMonitor m_dependencyMonitor; LmbrCentral::DependencyMonitor m_dependencyMonitor;
}; };

@ -124,39 +124,6 @@ namespace GradientSignal
void SetFallOffMidpoint(float midpoint) override; void SetFallOffMidpoint(float midpoint) override;
private: private:
float GetSlopeRatio(const SurfaceData::SurfacePointList& points, float angleMin, float angleMax) const
{
constexpr size_t inPositionIndex = 0;
if (points.IsEmpty(inPositionIndex))
{
return 0.0f;
}
// Assuming our surface normal vector is actually normalized, we can get the slope
// by just grabbing the Z value. It's the same thing as normal.Dot(AZ::Vector3::CreateAxisZ()).
auto highestSurfacePoint = points.GetHighestSurfacePoint(inPositionIndex);
AZ_Assert(
highestSurfacePoint.m_normal.GetNormalized().IsClose(highestSurfacePoint.m_normal),
"Surface normals are expected to be normalized");
const float slope = highestSurfacePoint.m_normal.GetZ();
// Convert slope back to an angle so that we can lerp in "angular space", not "slope value space".
// (We want our 0-1 range to be linear across the range of angles)
const float slopeAngle = acosf(slope);
switch (m_configuration.m_rampType)
{
case SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP:
return m_configuration.m_smoothStep.GetSmoothedValue(GetRatio(angleMin, angleMax, slopeAngle));
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP:
// For ramp up, linearly interpolate from min to max.
return GetRatio(angleMin, angleMax, slopeAngle);
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN:
default:
// For ramp down, linearly interpolate from max to min.
return GetRatio(angleMax, angleMin, slopeAngle);
}
}
SurfaceSlopeGradientConfig m_configuration; SurfaceSlopeGradientConfig m_configuration;
}; };
} }

@ -202,18 +202,9 @@ namespace GradientSignal
float SurfaceAltitudeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const float SurfaceAltitudeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
{ {
// For GetValue(), we reuse our SurfacePointList for the GetSurfacePoints query to avoid the repeated cost of memory allocation float result = 0.0f;
// and deallocation across GetValue() calls. However, this also means we can only use it from one thread at a time, so lock a GetValues(AZStd::span<const AZ::Vector3>(&sampleParams.m_position, 1), AZStd::span<float>(&result, 1));
// mutex to ensure no other threads call GetValue() at the same time. return result;
AZStd::unique_lock<decltype(m_surfacePointListMutex)> surfacePointLock(m_surfacePointListMutex);
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints,
sampleParams.m_position, m_configuration.m_surfaceTagsToSample, m_surfacePointList);
constexpr size_t inPositionIndex = 0;
return CalculateAltitudeRatio(m_surfacePointList, inPositionIndex, m_configuration.m_altitudeMin, m_configuration.m_altitudeMax);
} }
void SurfaceAltitudeGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const void SurfaceAltitudeGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
@ -231,17 +222,20 @@ namespace GradientSignal
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, positions, m_configuration.m_surfaceTagsToSample, &SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, positions, m_configuration.m_surfaceTagsToSample,
points); points);
if (points.IsEmpty()) // For each position, turn the height into a 0-1 value based on our min/max altitudes.
for (size_t index = 0; index < positions.size(); index++)
{ {
// No surface data, so no output values. if (!points.IsEmpty(index))
AZStd::fill(outValues.begin(), outValues.end(), 0.0f); {
// Get the point with the highest Z value and use that for the altitude.
const float highestAltitude = points.GetHighestSurfacePoint(index).m_position.GetZ();
// Turn the absolute altitude value into a 0-1 value by returning the % of the given altitude range that it falls at.
outValues[index] = GetRatio(m_configuration.m_altitudeMin, m_configuration.m_altitudeMax, highestAltitude);
} }
else else
{ {
// For each position, turn the height into a 0-1 value based on our min/max altitudes. outValues[index] = 0.0f;
for (size_t index = 0; index < positions.size(); index++)
{
outValues[index] = CalculateAltitudeRatio(points, index, m_configuration.m_altitudeMin, m_configuration.m_altitudeMax);
} }
} }
} }

@ -162,16 +162,7 @@ namespace GradientSignal
float SurfaceMaskGradientComponent::GetValue(const GradientSampleParams& params) const float SurfaceMaskGradientComponent::GetValue(const GradientSampleParams& params) const
{ {
float result = 0.0f; float result = 0.0f;
GetValues(AZStd::span<const AZ::Vector3>(&params.m_position, 1), AZStd::span<float>(&result, 1));
if (!m_configuration.m_surfaceTagList.empty())
{
SurfaceData::SurfacePointList points;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints,
params.m_position, m_configuration.m_surfaceTagList, points);
result = GetMaxSurfaceWeight(points);
}
return result; return result;
} }
@ -183,34 +174,31 @@ namespace GradientSignal
return; return;
} }
bool valuesFound = false; // Initialize all our output values to 0.
AZStd::fill(outValues.begin(), outValues.end(), 0.0f);
if (!m_configuration.m_surfaceTagList.empty()) if (!m_configuration.m_surfaceTagList.empty())
{ {
// Rather than calling GetSurfacePoints on the EBus repeatedly in a loop, we instead pass a lambda into the EBus that contains
// the loop within it so that we can avoid the repeated EBus-calling overhead.
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
[this, positions, &outValues, &valuesFound](SurfaceData::SurfaceDataSystemRequestBus::Events* surfaceDataRequests)
{
// It's possible that there's nothing connected to the EBus, so keep track of the fact that we have valid results.
valuesFound = true;
SurfaceData::SurfacePointList points; SurfaceData::SurfacePointList points;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, positions, m_configuration.m_surfaceTagList,
points);
for (size_t index = 0; index < positions.size(); index++) // For each position, get the max surface weight that matches our filter and that appears at that position.
points.EnumeratePoints(
[&outValues](
size_t inPositionIndex, [[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal,
const SurfaceData::SurfaceTagWeights& masks) -> bool
{ {
points.Clear(); masks.EnumerateWeights(
surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagList, points); [inPositionIndex, &outValues]([[maybe_unused]] AZ::Crc32 surfaceType, float weight) -> bool
outValues[index] = GetMaxSurfaceWeight(points);
}
});
}
if (!valuesFound)
{ {
// No surface tags, so no output values. outValues[inPositionIndex] = AZ::GetMax(AZ::GetClamp(weight, 0.0f, 1.0f), outValues[inPositionIndex]);
AZStd::fill(outValues.begin(), outValues.end(), 0.0f); return true;
});
return true;
});
} }
} }
size_t SurfaceMaskGradientComponent::GetNumTags() const size_t SurfaceMaskGradientComponent::GetNumTags() const

@ -205,14 +205,9 @@ namespace GradientSignal
float SurfaceSlopeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const float SurfaceSlopeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
{ {
SurfaceData::SurfacePointList points; float result = 0.0f;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePoints, GetValues(AZStd::span<const AZ::Vector3>(&sampleParams.m_position, 1), AZStd::span<float>(&result, 1));
sampleParams.m_position, m_configuration.m_surfaceTagsToSample, points); return result;
const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f));
const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f));
return GetSlopeRatio(points, angleMin, angleMax);
} }
void SurfaceSlopeGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const void SurfaceSlopeGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
@ -223,32 +218,49 @@ namespace GradientSignal
return; return;
} }
bool valuesFound = false;
// Rather than calling GetSurfacePoints on the EBus repeatedly in a loop, we instead pass a lambda into the EBus that contains
// the loop within it so that we can avoid the repeated EBus-calling overhead.
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
[this, positions, &outValues, &valuesFound](SurfaceData::SurfaceDataSystemRequestBus::Events* surfaceDataRequests)
{
// It's possible that there's nothing connected to the EBus, so keep track of the fact that we have valid results.
valuesFound = true;
SurfaceData::SurfacePointList points; SurfaceData::SurfacePointList points;
SurfaceData::SurfaceDataSystemRequestBus::Broadcast(
&SurfaceData::SurfaceDataSystemRequestBus::Events::GetSurfacePointsFromList, positions, m_configuration.m_surfaceTagsToSample,
points);
const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f)); const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f));
const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f)); const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f));
for (size_t index = 0; index < positions.size(); index++) for (size_t index = 0; index < positions.size(); index++)
{ {
points.Clear(); if (points.IsEmpty(index))
surfaceDataRequests->GetSurfacePoints(positions[index], m_configuration.m_surfaceTagsToSample, points); {
outValues[index] = GetSlopeRatio(points, angleMin, angleMax); outValues[index] = 0.0f;
} }
}); else
{
// Assuming our surface normal vector is actually normalized, we can get the slope
// by just grabbing the Z value. It's the same thing as normal.Dot(AZ::Vector3::CreateAxisZ()).
auto highestSurfacePoint = points.GetHighestSurfacePoint(index);
AZ_Assert(
highestSurfacePoint.m_normal.GetNormalized().IsClose(highestSurfacePoint.m_normal),
"Surface normals are expected to be normalized");
const float slope = highestSurfacePoint.m_normal.GetZ();
// Convert slope back to an angle so that we can lerp in "angular space", not "slope value space".
// (We want our 0-1 range to be linear across the range of angles)
const float slopeAngle = acosf(slope);
if (!valuesFound) switch (m_configuration.m_rampType)
{ {
// No surface tags, so no output values. case SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP:
AZStd::fill(outValues.begin(), outValues.end(), 0.0f); outValues[index] = m_configuration.m_smoothStep.GetSmoothedValue(GetRatio(angleMin, angleMax, slopeAngle));
break;
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP:
// For ramp up, linearly interpolate from min to max.
outValues[index] = GetRatio(angleMin, angleMax, slopeAngle);
break;
case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN:
default:
// For ramp down, linearly interpolate from max to min.
outValues[index] = GetRatio(angleMax, angleMin, slopeAngle);
break;
}
}
} }
} }

@ -108,6 +108,7 @@ namespace GradientSignal
// Create a fake surface point with the position we're sampling. // Create a fake surface point with the position we're sampling.
AzFramework::SurfaceData::SurfacePoint point; AzFramework::SurfaceData::SurfacePoint point;
point.m_position = params.m_position; point.m_position = params.m_position;
point.m_normal = AZ::Vector3::CreateAxisZ();
SurfaceData::SurfacePointList pointList = AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&point, 1); SurfaceData::SurfacePointList pointList = AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&point, 1);
// Send it into the component, see what emerges // Send it into the component, see what emerges

@ -78,6 +78,7 @@ namespace UnitTest
for (int x = 0; x < dataSize; x++) for (int x = 0; x < dataSize; x++)
{ {
float angle = AZ::DegToRad(inputAngles[(y * dataSize) + x]); float angle = AZ::DegToRad(inputAngles[(y * dataSize) + x]);
point.m_position = AZ::Vector3(aznumeric_cast<float>(x), aznumeric_cast<float>(y), 0.0f);
point.m_normal = AZ::Vector3(sinf(angle), 0.0f, cosf(angle)); point.m_normal = AZ::Vector3(sinf(angle), 0.0f, cosf(angle));
mockSurface->m_surfacePoints[AZStd::make_pair(static_cast<float>(x), static_cast<float>(y))] = mockSurface->m_surfacePoints[AZStd::make_pair(static_cast<float>(x), static_cast<float>(y))] =
AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&point, 1); AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&point, 1);
@ -549,10 +550,10 @@ namespace UnitTest
mockSurface->m_bounds = mockShapeComponentHandler.m_GetEncompassingAabb; mockSurface->m_bounds = mockShapeComponentHandler.m_GetEncompassingAabb;
AzFramework::SurfaceData::SurfacePoint mockOutputs[] = AzFramework::SurfaceData::SurfacePoint mockOutputs[] =
{ {
{ AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateAxisZ() },
}; };
mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] =
@ -597,10 +598,10 @@ namespace UnitTest
auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>(); auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>();
mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f)); mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f));
AzFramework::SurfaceData::SurfacePoint mockOutputs[] = { AzFramework::SurfaceData::SurfacePoint mockOutputs[] = {
{ AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 2.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 5.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 10.0f), AZ::Vector3::CreateAxisZ() },
}; };
mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] = mockSurface->m_surfacePoints[AZStd::make_pair(0.0f, 0.0f)] =
@ -667,10 +668,10 @@ namespace UnitTest
auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>(); auto mockSurface = surfaceEntity->CreateComponent<MockSurfaceProviderComponent>();
mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f)); mockSurface->m_bounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(1.0f));
AzFramework::SurfaceData::SurfacePoint mockOutputs[] = { AzFramework::SurfaceData::SurfacePoint mockOutputs[] = {
{ AZ::Vector3(0.0f, 0.0f, -10.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, -10.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, -5.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, -5.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 15.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 15.0f), AZ::Vector3::CreateAxisZ() },
{ AZ::Vector3(0.0f, 0.0f, 20.0f), AZ::Vector3::CreateZero() }, { AZ::Vector3(0.0f, 0.0f, 20.0f), AZ::Vector3::CreateAxisZ() },
}; };
// Altitude value below min - should result in 0.0f. // Altitude value below min - should result in 0.0f.
@ -724,6 +725,8 @@ namespace UnitTest
{ {
for (int x = 0; x < dataSize; x++) for (int x = 0; x < dataSize; x++)
{ {
point.m_position = AZ::Vector3(aznumeric_cast<float>(x), aznumeric_cast<float>(y), 0.0f);
point.m_normal = AZ::Vector3::CreateAxisZ();
point.m_surfaceTags.clear(); point.m_surfaceTags.clear();
point.m_surfaceTags.emplace_back(AZ_CRC_CE("test_mask"), expectedOutput[(y * dataSize) + x]); point.m_surfaceTags.emplace_back(AZ_CRC_CE("test_mask"), expectedOutput[(y * dataSize) + x]);
mockSurface->m_surfacePoints[AZStd::make_pair(static_cast<float>(x), static_cast<float>(y))] = mockSurface->m_surfacePoints[AZStd::make_pair(static_cast<float>(x), static_cast<float>(y))] =

@ -9,6 +9,8 @@
//#include "CustomizeKeyboardPage.h" //#include "CustomizeKeyboardPage.h"
#include <AzCore/Math/Color.h>
#include <Util/EditorUtils.h> #include <Util/EditorUtils.h>
#include <Editor/Resource.h> #include <Editor/Resource.h>
@ -99,9 +101,9 @@ QColor ColorLinearToGamma(ColorF col)
float g = clamp_tpl(col.g, 0.0f, 1.0f); float g = clamp_tpl(col.g, 0.0f, 1.0f);
float b = clamp_tpl(col.b, 0.0f, 1.0f); float b = clamp_tpl(col.b, 0.0f, 1.0f);
r = (float)(r <= 0.0031308 ? (12.92 * r) : (1.055 * pow((double)r, 1.0 / 2.4) - 0.055)); r = AZ::Color::ConvertSrgbLinearToGamma(r);
g = (float)(g <= 0.0031308 ? (12.92 * g) : (1.055 * pow((double)g, 1.0 / 2.4) - 0.055)); g = AZ::Color::ConvertSrgbLinearToGamma(g);
b = (float)(b <= 0.0031308 ? (12.92 * b) : (1.055 * pow((double)b, 1.0 / 2.4) - 0.055)); b = AZ::Color::ConvertSrgbLinearToGamma(b);
return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f)); return QColor(int(r * 255.0f), int(g * 255.0f), int(b * 255.0f));
} }
@ -113,7 +115,7 @@ ColorF ColorGammaToLinear(const QColor& col)
float g = (float)col.green() / 255.0f; float g = (float)col.green() / 255.0f;
float b = (float)col.blue() / 255.0f; float b = (float)col.blue() / 255.0f;
return ColorF((float)(r <= 0.04045 ? (r / 12.92) : pow(((double)r + 0.055) / 1.055, 2.4)), return ColorF(AZ::Color::ConvertSrgbGammaToLinear(r),
(float)(g <= 0.04045 ? (g / 12.92) : pow(((double)g + 0.055) / 1.055, 2.4)), AZ::Color::ConvertSrgbGammaToLinear(g),
(float)(b <= 0.04045 ? (b / 12.92) : pow(((double)b + 0.055) / 1.055, 2.4))); AZ::Color::ConvertSrgbGammaToLinear(b));
} }

@ -82,6 +82,7 @@ namespace SurfaceData
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus // SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override; void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override;
void GetSurfacePointsFromList(AZStd::span<const AZ::Vector3> inPositions, SurfacePointList& surfacePointList) const override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// SurfaceDataModifierRequestBus // SurfaceDataModifierRequestBus

@ -66,6 +66,7 @@ namespace SurfaceData
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus // SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override; void GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const override;
void GetSurfacePointsFromList(AZStd::span<const AZ::Vector3> inPositions, SurfacePointList& surfacePointList) const override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// SurfaceDataModifierRequestBus // SurfaceDataModifierRequestBus

@ -185,8 +185,6 @@ namespace SurfaceData
bool SurfaceDataColliderComponent::DoRayTrace(const AZ::Vector3& inPosition, bool queryPointOnly, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const bool SurfaceDataColliderComponent::DoRayTrace(const AZ::Vector3& inPosition, bool queryPointOnly, AZ::Vector3& outPosition, AZ::Vector3& outNormal) const
{ {
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
// test AABB as first pass to claim the point // test AABB as first pass to claim the point
const AZ::Vector3 testPosition = AZ::Vector3( const AZ::Vector3 testPosition = AZ::Vector3(
inPosition.GetX(), inPosition.GetX(),
@ -229,18 +227,45 @@ namespace SurfaceData
void SurfaceDataColliderComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const void SurfaceDataColliderComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{ {
AZ::Vector3 hitPosition; GetSurfacePointsFromList(AZStd::span<const AZ::Vector3>(&inPosition, 1), surfacePointList);
AZ::Vector3 hitNormal; }
void SurfaceDataColliderComponent::GetSurfacePointsFromList(
AZStd::span<const AZ::Vector3> inPositions, SurfacePointList& surfacePointList) const
{
AzPhysics::SimulatedBodyComponentRequestsBus::Event(
GetEntityId(),
[this, inPositions, &surfacePointList](AzPhysics::SimulatedBodyComponentRequestsBus::Events* simBody)
{
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
AzPhysics::RayCastRequest request;
request.m_direction = -AZ::Vector3::CreateAxisZ();
for (auto& inPosition : inPositions)
{
// test AABB as first pass to claim the point
if (SurfaceData::AabbContains2D(m_colliderBounds, inPosition))
{
// 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();
// We want a full raycast, so don't just query the start point. AzPhysics::SceneQueryHit result = simBody->RayCast(request);
constexpr bool queryPointOnly = false;
if (DoRayTrace(inPosition, queryPointOnly, hitPosition, hitNormal)) if (result)
{ {
surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, hitPosition, hitNormal, m_newPointWeights); surfacePointList.AddSurfacePoint(
GetEntityId(), inPosition, result.m_position, result.m_normal, m_newPointWeights);
}
}
} }
});
} }
void SurfaceDataColliderComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const void SurfaceDataColliderComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const
{ {
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex); AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);

@ -143,24 +143,48 @@ namespace SurfaceData
} }
void SurfaceDataShapeComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const void SurfaceDataShapeComponent::GetSurfacePoints(const AZ::Vector3& inPosition, SurfacePointList& surfacePointList) const
{
GetSurfacePointsFromList(AZStd::span<const AZ::Vector3>(&inPosition, 1), surfacePointList);
}
void SurfaceDataShapeComponent::GetSurfacePointsFromList(
AZStd::span<const AZ::Vector3> inPositions, SurfacePointList& surfacePointList) const
{ {
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex); AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);
if (m_shapeBoundsIsValid && SurfaceData::AabbContains2D(m_shapeBounds, inPosition)) if (!m_shapeBoundsIsValid)
{
return;
}
LmbrCentral::ShapeComponentRequestsBus::Event(
GetEntityId(),
[this, inPositions, &surfacePointList](LmbrCentral::ShapeComponentRequestsBus::Events* shape)
{ {
const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_shapeBounds.GetMax().GetZ());
const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ(); const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ();
// Shapes don't currently have a way to query normals at a point intersection, so we'll just return a Z-up normal
// until they get support for it.
const AZ::Vector3 surfacePointNormal = AZ::Vector3::CreateAxisZ();
for (auto& inPosition : inPositions)
{
if (SurfaceData::AabbContains2D(m_shapeBounds, inPosition))
{
const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_shapeBounds.GetMax().GetZ());
float intersectionDistance = 0.0f; float intersectionDistance = 0.0f;
bool hitShape = false; bool hitShape = shape->IntersectRay(rayOrigin, rayDirection, intersectionDistance);
LmbrCentral::ShapeComponentRequestsBus::EventResult(hitShape, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IntersectRay, rayOrigin, rayDirection, intersectionDistance);
if (hitShape) if (hitShape)
{ {
AZ::Vector3 position = rayOrigin + intersectionDistance * rayDirection; AZ::Vector3 position = rayOrigin + intersectionDistance * rayDirection;
surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, position, AZ::Vector3::CreateAxisZ(), m_newPointWeights); surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, position, surfacePointNormal, m_newPointWeights);
}
} }
} }
});
} }
void SurfaceDataShapeComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const void SurfaceDataShapeComponent::ModifySurfacePoints(SurfacePointList& surfacePointList) const
{ {
AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex); AZStd::shared_lock<decltype(m_cacheMutex)> lock(m_cacheMutex);

@ -32,7 +32,11 @@ namespace SurfaceData
inPositionIndex = (inPositionIndex + 1) % m_inputPositions.size(); inPositionIndex = (inPositionIndex + 1) % m_inputPositions.size();
} }
AZ_Assert(foundMatch, "Couldn't find input position!"); AZ_Assert(
foundMatch,
"Couldn't find input position: (%0.7f, %0.7f, %0.7f), m_lastInputPositionIndex = %zu, m_inputPositions.size() = %zu",
inPosition.GetX(), inPosition.GetY(), inPosition.GetZ(), m_lastInputPositionIndex, m_inputPositions.size());
m_lastInputPositionIndex = inPositionIndex; m_lastInputPositionIndex = inPositionIndex;
return inPositionIndex; return inPositionIndex;
} }

@ -145,29 +145,42 @@ namespace Terrain
void TerrainSurfaceDataSystemComponent::GetSurfacePoints( void TerrainSurfaceDataSystemComponent::GetSurfacePoints(
const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const
{
GetSurfacePointsFromList(AZStd::span<const AZ::Vector3>(&inPosition, 1), surfacePointList);
}
void TerrainSurfaceDataSystemComponent::GetSurfacePointsFromList(
AZStd::span<const AZ::Vector3> inPositions, SurfaceData::SurfacePointList& surfacePointList) const
{ {
if (!m_terrainBoundsIsValid) if (!m_terrainBoundsIsValid)
{ {
return; return;
} }
bool isTerrainValidAtPoint = false; size_t inPositionIndex = 0;
AzFramework::SurfaceData::SurfacePoint terrainSurfacePoint;
AzFramework::Terrain::TerrainDataRequestBus::Broadcast(&AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfacePoint,
inPosition, terrainSurfacePoint, AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR,
&isTerrainValidAtPoint);
const bool isHole = !isTerrainValidAtPoint; AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
&AzFramework::Terrain::TerrainDataRequestBus::Events::ProcessSurfacePointsFromList, inPositions,
[this, inPositions, &inPositionIndex, &surfacePointList]
(const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
{
AZ_Assert(inPositionIndex < inPositions.size(), "Too many points returned from ProcessSurfacePointsFromList");
SurfaceData::SurfaceTagWeights weights(terrainSurfacePoint.m_surfaceTags); SurfaceData::SurfaceTagWeights weights(surfacePoint.m_surfaceTags);
// Always add a "terrain" or "terrainHole" tag. // Always add a "terrain" or "terrainHole" tag.
const AZ::Crc32 terrainTag = isHole ? Constants::s_terrainHoleTagCrc : Constants::s_terrainTagCrc; const AZ::Crc32 terrainTag = terrainExists ? Constants::s_terrainTagCrc : Constants::s_terrainHoleTagCrc;
weights.AddSurfaceTagWeight(terrainTag, 1.0f); weights.AddSurfaceTagWeight(terrainTag, 1.0f);
surfacePointList.AddSurfacePoint(GetEntityId(), inPosition, terrainSurfacePoint.m_position, terrainSurfacePoint.m_normal, weights); surfacePointList.AddSurfacePoint(
GetEntityId(), inPositions[inPositionIndex], surfacePoint.m_position, surfacePoint.m_normal, weights);
inPositionIndex++;
},
AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR);
} }
AZ::Aabb TerrainSurfaceDataSystemComponent::GetSurfaceAabb() const AZ::Aabb TerrainSurfaceDataSystemComponent::GetSurfaceAabb() const
{ {
auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler(); auto terrain = AzFramework::Terrain::TerrainDataRequestBus::FindFirstHandler();

@ -59,6 +59,8 @@ namespace Terrain
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// SurfaceDataProviderRequestBus // SurfaceDataProviderRequestBus
void GetSurfacePoints(const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const override; void GetSurfacePoints(const AZ::Vector3& inPosition, SurfaceData::SurfacePointList& surfacePointList) const override;
void GetSurfacePointsFromList(
AZStd::span<const AZ::Vector3> inPositions, SurfaceData::SurfacePointList& surfacePointList) const override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// AzFramework::Terrain::TerrainDataNotificationBus // AzFramework::Terrain::TerrainDataNotificationBus

@ -192,7 +192,7 @@ bool TerrainSystem::InWorldBounds(float x, float y) const
} }
// Generate positions to be queried based on the sampler type. // Generate positions to be queried based on the sampler type.
void TerrainSystem::GenerateQueryPositions(const AZStd::span<AZ::Vector3>& inPositions, void TerrainSystem::GenerateQueryPositions(const AZStd::span<const AZ::Vector3>& inPositions,
AZStd::vector<AZ::Vector3>& outPositions, AZStd::vector<AZ::Vector3>& outPositions,
Sampler sampler) const Sampler sampler) const
{ {
@ -253,7 +253,7 @@ AZStd::vector<AZ::Vector3> TerrainSystem::GenerateInputPositionsFromRegion(
} }
void TerrainSystem::MakeBulkQueries( void TerrainSystem::MakeBulkQueries(
const AZStd::span<AZ::Vector3> inPositions, const AZStd::span<const AZ::Vector3> inPositions,
AZStd::span<AZ::Vector3> outPositions, AZStd::span<AZ::Vector3> outPositions,
AZStd::span<bool> outTerrainExists, AZStd::span<bool> outTerrainExists,
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights, AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
@ -296,7 +296,7 @@ void TerrainSystem::MakeBulkQueries(
if (prevAreaId != AZ::EntityId()) if (prevAreaId != AZ::EntityId())
{ {
size_t spanLength = (windowEnd - windowStart) + 1; size_t spanLength = (windowEnd - windowStart) + 1;
queryCallback(AZStd::span<AZ::Vector3>(inPositions.begin() + windowStart, spanLength), queryCallback(AZStd::span<const AZ::Vector3>(inPositions.begin() + windowStart, spanLength),
AZStd::span<AZ::Vector3>(outPositions.begin() + windowStart, spanLength), AZStd::span<AZ::Vector3>(outPositions.begin() + windowStart, spanLength),
AZStd::span<bool>(outTerrainExists.begin() + windowStart, spanLength), AZStd::span<bool>(outTerrainExists.begin() + windowStart, spanLength),
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList>(outSurfaceWeights.begin() + windowStart, spanLength), AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList>(outSurfaceWeights.begin() + windowStart, spanLength),
@ -311,7 +311,7 @@ void TerrainSystem::MakeBulkQueries(
} }
} }
void TerrainSystem::GetHeightsSynchronous(const AZStd::span<AZ::Vector3>& inPositions, Sampler sampler, void TerrainSystem::GetHeightsSynchronous(const AZStd::span<const AZ::Vector3>& inPositions, Sampler sampler,
AZStd::span<float> heights, AZStd::span<bool> terrainExists) const AZStd::span<float> heights, AZStd::span<bool> terrainExists) const
{ {
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex); AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
@ -328,7 +328,7 @@ void TerrainSystem::GetHeightsSynchronous(const AZStd::span<AZ::Vector3>& inPosi
GenerateQueryPositions(inPositions, outPositions, sampler); GenerateQueryPositions(inPositions, outPositions, sampler);
auto callback = []([[maybe_unused]] const AZStd::span<AZ::Vector3> inPositions, auto callback = []([[maybe_unused]] const AZStd::span<const AZ::Vector3> inPositions,
AZStd::span<AZ::Vector3> outPositions, AZStd::span<AZ::Vector3> outPositions,
AZStd::span<bool> outTerrainExists, AZStd::span<bool> outTerrainExists,
[[maybe_unused]] AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights, [[maybe_unused]] AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
@ -516,7 +516,7 @@ bool TerrainSystem::GetIsHoleFromFloats(float x, float y, Sampler sampler) const
return !terrainExists; return !terrainExists;
} }
void TerrainSystem::GetNormalsSynchronous(const AZStd::span<AZ::Vector3>& inPositions, Sampler sampler, void TerrainSystem::GetNormalsSynchronous(const AZStd::span<const AZ::Vector3>& inPositions, Sampler sampler,
AZStd::span<AZ::Vector3> normals, AZStd::span<bool> terrainExists) const AZStd::span<AZ::Vector3> normals, AZStd::span<bool> terrainExists) const
{ {
AZStd::vector<AZ::Vector3> directionVectors; AZStd::vector<AZ::Vector3> directionVectors;
@ -685,7 +685,7 @@ AzFramework::RenderGeometry::RayResult TerrainSystem::GetClosestIntersection(
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessHeightsFromListAsync( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessHeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -695,7 +695,7 @@ AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext>
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessNormalsFromListAsync( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessNormalsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -705,7 +705,7 @@ AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext>
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfaceWeightsFromListAsync( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfaceWeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -715,7 +715,7 @@ AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext>
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfacePointsFromListAsync( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfacePointsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -725,7 +725,7 @@ AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext>
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessHeightsFromListOfVector2Async( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessHeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -735,7 +735,7 @@ AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext>
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessNormalsFromListOfVector2Async( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessNormalsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -745,7 +745,7 @@ AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext>
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfaceWeightsFromListOfVector2Async( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfaceWeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -755,7 +755,7 @@ AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext>
} }
AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfacePointsFromListOfVector2Async( AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessSurfacePointsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -830,7 +830,7 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A
} }
void TerrainSystem::GetOrderedSurfaceWeightsFromList( void TerrainSystem::GetOrderedSurfaceWeightsFromList(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
[[maybe_unused]] Sampler sampler, [[maybe_unused]] Sampler sampler,
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList, AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList,
AZStd::span<bool> terrainExists) const AZStd::span<bool> terrainExists) const
@ -841,7 +841,7 @@ void TerrainSystem::GetOrderedSurfaceWeightsFromList(
GetHeightsSynchronous(inPositions, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, heights, terrainExists); GetHeightsSynchronous(inPositions, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, heights, terrainExists);
} }
auto callback = [](const AZStd::span<AZ::Vector3> inPositions, auto callback = [](const AZStd::span<const AZ::Vector3> inPositions,
[[maybe_unused]] AZStd::span<AZ::Vector3> outPositions, [[maybe_unused]] AZStd::span<AZ::Vector3> outPositions,
[[maybe_unused]] AZStd::span<bool> outTerrainExists, [[maybe_unused]] AZStd::span<bool> outTerrainExists,
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights, AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
@ -929,7 +929,7 @@ const char* TerrainSystem::GetMaxSurfaceName(
} }
void TerrainSystem::ProcessHeightsFromList( void TerrainSystem::ProcessHeightsFromList(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {
@ -953,7 +953,7 @@ void TerrainSystem::ProcessHeightsFromList(
} }
void TerrainSystem::ProcessNormalsFromList( void TerrainSystem::ProcessNormalsFromList(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {
@ -977,7 +977,7 @@ void TerrainSystem::ProcessNormalsFromList(
} }
void TerrainSystem::ProcessSurfaceWeightsFromList( void TerrainSystem::ProcessSurfaceWeightsFromList(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {
@ -1001,7 +1001,7 @@ void TerrainSystem::ProcessSurfaceWeightsFromList(
} }
void TerrainSystem::ProcessSurfacePointsFromList( void TerrainSystem::ProcessSurfacePointsFromList(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {
@ -1034,7 +1034,7 @@ void TerrainSystem::ProcessSurfacePointsFromList(
} }
void TerrainSystem::ProcessHeightsFromListOfVector2( void TerrainSystem::ProcessHeightsFromListOfVector2(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {
@ -1054,7 +1054,7 @@ void TerrainSystem::ProcessHeightsFromListOfVector2(
} }
void TerrainSystem::ProcessNormalsFromListOfVector2( void TerrainSystem::ProcessNormalsFromListOfVector2(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {
@ -1074,7 +1074,7 @@ void TerrainSystem::ProcessNormalsFromListOfVector2(
} }
void TerrainSystem::ProcessSurfaceWeightsFromListOfVector2( void TerrainSystem::ProcessSurfaceWeightsFromListOfVector2(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {
@ -1094,7 +1094,7 @@ void TerrainSystem::ProcessSurfaceWeightsFromListOfVector2(
} }
void TerrainSystem::ProcessSurfacePointsFromListOfVector2( void TerrainSystem::ProcessSurfacePointsFromListOfVector2(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter) const Sampler sampleFilter) const
{ {

@ -140,28 +140,28 @@ namespace Terrain
//! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each //! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each
//! XY coordinate in the list. //! XY coordinate in the list.
virtual void ProcessHeightsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessHeightsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
virtual void ProcessNormalsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessNormalsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
virtual void ProcessSurfaceWeightsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessSurfaceWeightsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
virtual void ProcessSurfacePointsFromList(const AZStd::span<AZ::Vector3>& inPositions, virtual void ProcessSurfacePointsFromList(const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
virtual void ProcessHeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessHeightsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
virtual void ProcessNormalsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessNormalsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessSurfaceWeightsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span<AZ::Vector2>& inPositions, virtual void ProcessSurfacePointsFromListOfVector2(const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT) const override; Sampler sampleFilter = Sampler::DEFAULT) const override;
@ -194,42 +194,42 @@ namespace Terrain
const AzFramework::RenderGeometry::RayRequest& ray) const override; const AzFramework::RenderGeometry::RayRequest& ray) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListAsync( AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListAsync( AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListAsync( AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListAsync( AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListAsync(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListOfVector2Async( AZStd::shared_ptr<TerrainJobContext> ProcessHeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListOfVector2Async( AZStd::shared_ptr<TerrainJobContext> ProcessNormalsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListOfVector2Async( AZStd::shared_ptr<TerrainJobContext> ProcessSurfaceWeightsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListOfVector2Async( AZStd::shared_ptr<TerrainJobContext> ProcessSurfacePointsFromListOfVector2Async(
const AZStd::span<AZ::Vector2>& inPositions, const AZStd::span<const AZ::Vector2>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const override;
@ -262,7 +262,7 @@ namespace Terrain
template<typename SynchronousFunctionType, typename VectorType> template<typename SynchronousFunctionType, typename VectorType>
AZStd::shared_ptr<TerrainJobContext> ProcessFromListAsync( AZStd::shared_ptr<TerrainJobContext> ProcessFromListAsync(
SynchronousFunctionType synchronousFunction, SynchronousFunctionType synchronousFunction,
const AZStd::span<VectorType>& inPositions, const AZStd::span<const VectorType>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter = Sampler::DEFAULT, Sampler sampleFilter = Sampler::DEFAULT,
AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const; AZStd::shared_ptr<ProcessAsyncParams> params = nullptr) const;
@ -291,31 +291,31 @@ namespace Terrain
AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const; AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const;
typedef AZStd::function<void( typedef AZStd::function<void(
const AZStd::span<AZ::Vector3> inPositions, const AZStd::span<const AZ::Vector3> inPositions,
AZStd::span<AZ::Vector3> outPositions, AZStd::span<AZ::Vector3> outPositions,
AZStd::span<bool> outTerrainExists, AZStd::span<bool> outTerrainExists,
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights, AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
AZ::EntityId areaId)> BulkQueriesCallback; AZ::EntityId areaId)> BulkQueriesCallback;
void GetHeightsSynchronous( void GetHeightsSynchronous(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
Sampler sampler, AZStd::span<float> heights, Sampler sampler, AZStd::span<float> heights,
AZStd::span<bool> terrainExists) const; AZStd::span<bool> terrainExists) const;
void GetNormalsSynchronous( void GetNormalsSynchronous(
const AZStd::span<AZ::Vector3>& inPositions, const AZStd::span<const AZ::Vector3>& inPositions,
Sampler sampler, AZStd::span<AZ::Vector3> normals, Sampler sampler, AZStd::span<AZ::Vector3> normals,
AZStd::span<bool> terrainExists) const; AZStd::span<bool> terrainExists) const;
void GetOrderedSurfaceWeightsFromList( void GetOrderedSurfaceWeightsFromList(
const AZStd::span<AZ::Vector3>& inPositions, Sampler sampler, const AZStd::span<const AZ::Vector3>& inPositions, Sampler sampler,
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList, AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList,
AZStd::span<bool> terrainExists) const; AZStd::span<bool> terrainExists) const;
void MakeBulkQueries( void MakeBulkQueries(
const AZStd::span<AZ::Vector3> inPositions, const AZStd::span<const AZ::Vector3> inPositions,
AZStd::span<AZ::Vector3> outPositions, AZStd::span<AZ::Vector3> outPositions,
AZStd::span<bool> outTerrainExists, AZStd::span<bool> outTerrainExists,
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWieghts, AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWieghts,
BulkQueriesCallback queryCallback) const; BulkQueriesCallback queryCallback) const;
void GenerateQueryPositions(const AZStd::span<AZ::Vector3>& inPositions, void GenerateQueryPositions(const AZStd::span<const AZ::Vector3>& inPositions,
AZStd::vector<AZ::Vector3>& outPositions, AZStd::vector<AZ::Vector3>& outPositions,
Sampler sampler) const; Sampler sampler) const;
AZStd::vector<AZ::Vector3> GenerateInputPositionsFromRegion( AZStd::vector<AZ::Vector3> GenerateInputPositionsFromRegion(
@ -361,7 +361,7 @@ namespace Terrain
template<typename SynchronousFunctionType, typename VectorType> template<typename SynchronousFunctionType, typename VectorType>
inline AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessFromListAsync( inline AZStd::shared_ptr<AzFramework::Terrain::TerrainDataRequests::TerrainJobContext> TerrainSystem::ProcessFromListAsync(
SynchronousFunctionType synchronousFunction, SynchronousFunctionType synchronousFunction,
const AZStd::span<VectorType>& inPositions, const AZStd::span<const VectorType>& inPositions,
AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback, AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
Sampler sampleFilter, Sampler sampleFilter,
AZStd::shared_ptr<ProcessAsyncParams> params) const AZStd::shared_ptr<ProcessAsyncParams> params) const
@ -397,7 +397,7 @@ namespace Terrain
const size_t subSpanCount = (i < numJobs - 1) ? numPositionsPerJob : AZStd::dynamic_extent; const size_t subSpanCount = (i < numJobs - 1) ? numPositionsPerJob : AZStd::dynamic_extent;
// Define the job function using the sub span of positions to process. // Define the job function using the sub span of positions to process.
const AZStd::span<VectorType>& positionsToProcess = inPositions.subspan(subSpanOffset, subSpanCount); const AZStd::span<const VectorType>& positionsToProcess = inPositions.subspan(subSpanOffset, subSpanCount);
auto jobFunction = [this, synchronousFunction, positionsToProcess, perPositionCallback, sampleFilter, jobContext, params]() auto jobFunction = [this, synchronousFunction, positionsToProcess, perPositionCallback, sampleFilter, jobContext, params]()
{ {
// Process the sub span of positions, unless the associated job context has been cancelled. // Process the sub span of positions, unless the associated job context has been cancelled.

Loading…
Cancel
Save