Merge branch 'development' into profiler_capture_api
Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com>
This commit is contained in:
-2
@@ -70,8 +70,6 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
|
||||
+76
-15
@@ -107,6 +107,19 @@ class EditorComponent:
|
||||
return type_ids
|
||||
|
||||
|
||||
|
||||
def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
|
||||
"""
|
||||
Converts a vector3-like element into a azlmbr.math.Vector3
|
||||
"""
|
||||
if isinstance(xyz, Tuple) or isinstance(xyz, List):
|
||||
assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
|
||||
return math.Vector3(float(xyz[0]), float(xyz[1]), float(xyz[2]))
|
||||
elif isinstance(xyz, type(math.Vector3())):
|
||||
return xyz
|
||||
else:
|
||||
raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
|
||||
|
||||
class EditorEntity:
|
||||
"""
|
||||
Entity class is used to create and interact with Editor Entities.
|
||||
@@ -183,15 +196,6 @@ class EditorEntity:
|
||||
:return: EditorEntity class object
|
||||
"""
|
||||
|
||||
def convert_to_azvector3(xyz) -> math.Vector3:
|
||||
if isinstance(xyz, Tuple) or isinstance(xyz, List):
|
||||
assert len(xyz) == 3, ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
|
||||
return math.Vector3(*xyz)
|
||||
elif isinstance(xyz, type(math.Vector3())):
|
||||
return xyz
|
||||
else:
|
||||
raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
|
||||
|
||||
if parent_id is None:
|
||||
parent_id = azlmbr.entity.EntityId()
|
||||
|
||||
@@ -206,7 +210,7 @@ class EditorEntity:
|
||||
return entity
|
||||
|
||||
# Methods
|
||||
def set_name(self, entity_name: str):
|
||||
def set_name(self, entity_name: str) -> None:
|
||||
"""
|
||||
Given entity_name, sets name to Entity
|
||||
:param: entity_name: Name of the entity to set
|
||||
@@ -324,7 +328,7 @@ class EditorEntity:
|
||||
self.start_status = status
|
||||
return status
|
||||
|
||||
def set_start_status(self, desired_start_status: str):
|
||||
def set_start_status(self, desired_start_status: str) -> None:
|
||||
"""
|
||||
Set an entity as active/inactive at beginning of runtime or it is editor-only,
|
||||
given its entity id and the start status then return set success
|
||||
@@ -382,18 +386,75 @@ class EditorEntity:
|
||||
"""
|
||||
return editor.EditorEntityInfoRequestBus(bus.Event, "IsVisible", self.id)
|
||||
|
||||
# World Transform Functions
|
||||
def get_world_translation(self) -> azlmbr.math.Vector3:
|
||||
"""
|
||||
Gets the world translation of the entity
|
||||
"""
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def set_world_translation(self, new_translation) -> None:
|
||||
"""
|
||||
Sets the new world translation of the current entity
|
||||
"""
|
||||
new_translation = convert_to_azvector3(new_translation)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetWorldTranslation", self.id, new_translation)
|
||||
|
||||
def get_world_rotation(self) -> azlmbr.math.Quaternion:
|
||||
"""
|
||||
Gets the world rotation of the entity
|
||||
"""
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", self.id)
|
||||
|
||||
def set_world_rotation(self, new_rotation):
|
||||
"""
|
||||
Sets the new world rotation of the current entity
|
||||
"""
|
||||
new_rotation = convert_to_azvector3(new_rotation)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetWorldRotation", self.id, new_rotation)
|
||||
|
||||
# Local Transform Functions
|
||||
def get_local_uniform_scale(self) -> float:
|
||||
"""
|
||||
Gets the local uniform scale of the entity
|
||||
"""
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalUniformScale", self.id)
|
||||
|
||||
def set_local_uniform_scale(self, scale_float) -> None:
|
||||
"""
|
||||
Sets the "SetLocalUniformScale" value on the entity.
|
||||
Sets the local uniform scale value(relative to the parent) on the entity.
|
||||
:param scale_float: value for "SetLocalUniformScale" to set to.
|
||||
:return: None
|
||||
"""
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", self.id, scale_float)
|
||||
|
||||
def set_local_rotation(self, vector3_rotation) -> None:
|
||||
def get_local_rotation(self) -> azlmbr.math.Quaternion:
|
||||
"""
|
||||
Sets the "SetLocalRotation" value on the entity.
|
||||
Gets the local rotation of the entity
|
||||
"""
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalRotation", self.id)
|
||||
|
||||
def set_local_rotation(self, new_rotation) -> None:
|
||||
"""
|
||||
Sets the set the local rotation(relative to the parent) of the current entity.
|
||||
:param vector3_rotation: The math.Vector3 value to use for rotation on the entity (uses radians).
|
||||
:return: None
|
||||
"""
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, vector3_rotation)
|
||||
new_rotation = convert_to_azvector3(new_rotation)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", self.id, new_rotation)
|
||||
|
||||
def get_local_translation(self) -> azlmbr.math.Vector3:
|
||||
"""
|
||||
Gets the local translation of the current entity.
|
||||
:return: The math.Vector3 value of the local translation.
|
||||
"""
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetLocalTranslation", self.id)
|
||||
|
||||
def set_local_translation(self, new_translation) -> None:
|
||||
"""
|
||||
Sets the local translation(relative to the parent) of the current entity.
|
||||
:param vector3_translation: The math.Vector3 value to use for translation on the entity.
|
||||
:return: None
|
||||
"""
|
||||
new_translation = convert_to_azvector3(new_translation)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalTranslation", self.id, new_translation)
|
||||
|
||||
+8
@@ -138,6 +138,14 @@ class PrefabInstance:
|
||||
self.container_entity = reparented_container_entity
|
||||
current_instance_prefab.instances.add(self)
|
||||
|
||||
def get_direct_child_entities(self):
|
||||
"""
|
||||
Returns the entities only contained in the current prefab instance.
|
||||
This function does not return entities contained in other child instances
|
||||
"""
|
||||
return self.container_entity.get_children()
|
||||
|
||||
|
||||
# This is a helper class which contains some of the useful information about a prefab template.
|
||||
class Prefab:
|
||||
|
||||
|
||||
@@ -55,3 +55,11 @@ class TestAutomation(TestAutomationBase):
|
||||
def test_PrefabBasicWorkflow_CreateAndDuplicatePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import PrefabBasicWorkflow_CreateAndDuplicatePrefab as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module)
|
||||
|
||||
def test_PrefabComplexWorflow_CreatePrefabOfChildEntity(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import PrefabComplexWorflow_CreatePrefabOfChildEntity as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
|
||||
|
||||
def test_PrefabComplexWorflow_CreatePrefabInsidePrefab(self, request, workspace, editor, launcher_platform):
|
||||
from .tests import PrefabComplexWorflow_CreatePrefabInsidePrefab as test_module
|
||||
self._run_prefab_test(request, workspace, editor, test_module, autotest_mode=False)
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
def PrefabComplexWorflow_CreatePrefabInsidePrefab():
|
||||
"""
|
||||
Test description:
|
||||
- Creates an entity with a physx collider
|
||||
- Creates a prefab "Outer_prefab" and an instance based of that entity
|
||||
- Creates a prefab "Inner_prefab" inside "Outer_prefab" based the entity contained inside of it
|
||||
Checks that the entity is correctly handlded by the prefab system checking the name and that it contains the physx collider
|
||||
"""
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
|
||||
import PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
# Creates a new Entity at the root level
|
||||
# Asserts if creation didn't succeed
|
||||
entity = EditorEntity.create_editor_entity_at((100.0, 100.0, 100.0), name = "TestEntity")
|
||||
assert entity.id.IsValid(), "Couldn't create entity"
|
||||
entity.add_component("PhysX Collider")
|
||||
assert entity.has_component("PhysX Collider"), "Attempted to add a PhysX Collider but no physx collider collider was found afterwards"
|
||||
|
||||
# Create a prefab based on that entity
|
||||
outer_prefab, outer_instance = Prefab.create_prefab([entity], "Outer_prefab")
|
||||
# The test should be now inside the outer prefab instance.
|
||||
entity = outer_instance.get_direct_child_entities()[0]
|
||||
# We track if that is the same entity by checking the name and if it still contains the component that we created before
|
||||
assert entity.get_name() == "TestEntity", f"Entity name inside outer_prefab doesn't match the original name, original:'TestEntity' current:'{entity.get_name()}'"
|
||||
assert entity.has_component("PhysX Collider"), "Entity name inside outer_prefab doesn't have the collider component it should"
|
||||
|
||||
# Now, create another prefab, based on the entity that is inside outer_prefab
|
||||
inner_prefab, inner_instance = Prefab.create_prefab([entity], "Inner_prefab")
|
||||
# The test entity should now be inside the inner prefab instance
|
||||
entity = inner_instance.get_direct_child_entities()[0]
|
||||
# We track if that is the same entity by checking the name and if it still contains the component that we created before
|
||||
assert entity.get_name() == "TestEntity", f"Entity name inside inner_prefab doesn't match the original name, original:'TestEntity' current:'{entity.get_name()}'"
|
||||
assert entity.has_component("PhysX Collider"), "Entity name inside inner_prefab doesn't have the collider component it should"
|
||||
|
||||
# Verify hierarchy of entities:
|
||||
# Outer_prefab
|
||||
# |- Inner_prefab
|
||||
# | |- TestEntity
|
||||
assert entity.get_parent_id() == inner_instance.container_entity.id
|
||||
assert inner_instance.container_entity.get_parent_id() == outer_instance.container_entity.id
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(PrefabComplexWorflow_CreatePrefabInsidePrefab)
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
def PrefabComplexWorflow_CreatePrefabOfChildEntity():
|
||||
"""
|
||||
Test description:
|
||||
- Creates two entities, parent and child. Child entity has Parent entity as its parent.
|
||||
- Creates a prefab of the child entity.
|
||||
Test is successful if the new instanced prefab of the child has the parent entity id
|
||||
"""
|
||||
|
||||
CAR_PREFAB_FILE_NAME = 'car_prefab'
|
||||
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.prefab_utils import Prefab
|
||||
|
||||
import PrefabTestUtils as prefab_test_utils
|
||||
|
||||
prefab_test_utils.open_base_tests_level()
|
||||
|
||||
# Creates a new Entity at the root level
|
||||
# Asserts if creation didn't succeed
|
||||
parent_entity = EditorEntity.create_editor_entity_at((100.0, 100.0, 100.0))
|
||||
assert parent_entity.id.IsValid(), "Couldn't create parent entity"
|
||||
|
||||
child_entity = EditorEntity.create_editor_entity(parent_id=parent_entity.id)
|
||||
assert child_entity.id.IsValid(), "Couldn't create child entity"
|
||||
assert child_entity.get_world_translation().IsClose(parent_entity.get_world_translation()), f"Child entity position{child_entity.get_world_translation().ToString()}" \
|
||||
f" is not located at the same position as the parent{parent_entity.get_world_translation().ToString()}"
|
||||
|
||||
# Asserts if prefab creation doesn't succeed
|
||||
child_prefab, child_instance = Prefab.create_prefab([child_entity], CAR_PREFAB_FILE_NAME)
|
||||
child_entity_on_child_instance = child_instance.get_direct_child_entities()[0]
|
||||
assert child_instance.container_entity.get_parent_id().IsValid(), "Newly instanced entity has no parent"
|
||||
assert child_instance.container_entity.get_parent_id() == parent_entity.id, "Newly instanced entity parent does not match the expected parent"
|
||||
assert child_instance.container_entity.get_world_translation().IsClose(parent_entity.get_world_translation()), "Newly instanced entity position is not located at the same position as the parent"
|
||||
# Move the parent position, it should update the child position
|
||||
parent_entity.set_world_translation((200.0, 200.0, 200.0))
|
||||
child_instance_translation = child_instance.container_entity.get_world_translation()
|
||||
assert child_instance_translation.IsClose(azlmbr.math.Vector3(200.0, 200.0, 200.0)), f"Instance position position{child_instance_translation.ToString()} didn't get updated" \
|
||||
f" to the same position as the parent{parent_entity.get_world_translation().ToString()}"
|
||||
child_translation = child_entity_on_child_instance.get_world_translation()
|
||||
assert child_translation.IsClose(azlmbr.math.Vector3(200.0, 200.0, 200.0)), f"Entity position{child_translation.ToString()} of the instance didn't get updated" \
|
||||
f" to the same position as the parent{parent_entity.get_world_translation().ToString()}"
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(PrefabComplexWorflow_CreatePrefabOfChildEntity)
|
||||
@@ -23,7 +23,7 @@ def create_jobs(request):
|
||||
jobDescriptorList = []
|
||||
for platformInfo in request.enabledPlatforms:
|
||||
jobDesc = azlmbr.asset.builder.JobDescriptor()
|
||||
jobDesc.jobKey = jobKeyName
|
||||
jobDesc.jobKey = f'{jobKeyName}-{platformInfo.identifier}'
|
||||
jobDesc.set_platform_identifier(platformInfo.identifier)
|
||||
jobDescriptorList.append(jobDesc)
|
||||
|
||||
@@ -38,7 +38,7 @@ def on_create_jobs(args):
|
||||
return create_jobs(request)
|
||||
except:
|
||||
log_exception_traceback()
|
||||
# returing back a default CreateJobsResponse() records an asset error
|
||||
# returning back a default CreateJobsResponse() records an asset error
|
||||
return azlmbr.asset.builder.CreateJobsResponse()
|
||||
|
||||
def process_file(request):
|
||||
@@ -58,6 +58,7 @@ def process_file(request):
|
||||
fileOutput = open(tempFilename, "w")
|
||||
fileOutput.write('{}')
|
||||
fileOutput.close()
|
||||
print(f'Wrote mock asset file: {tempFilename}')
|
||||
|
||||
# generate a product asset file entry
|
||||
subId = binascii.crc32(mockFilename.encode())
|
||||
|
||||
@@ -15,6 +15,7 @@ import sys
|
||||
import importlib
|
||||
import re
|
||||
|
||||
import ly_test_tools
|
||||
from ly_test_tools import LAUNCHERS
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
@@ -25,8 +26,15 @@ import ly_test_tools.environment.process_utils as process_utils
|
||||
|
||||
import argparse, sys
|
||||
|
||||
@pytest.mark.SUITE_main
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
def get_editor_launcher_platform():
|
||||
if ly_test_tools.WINDOWS:
|
||||
return "windows_editor"
|
||||
elif ly_test_tools.LINUX:
|
||||
return "linux_editor"
|
||||
else:
|
||||
return None
|
||||
|
||||
@pytest.mark.parametrize("launcher_platform", [get_editor_launcher_platform()])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
class TestEditorTest:
|
||||
|
||||
@@ -69,7 +77,7 @@ class TestEditorTest:
|
||||
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite
|
||||
|
||||
@pytest.mark.SUITE_main
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("launcher_platform", [{get_editor_launcher_platform()}])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
class test_single(EditorSingleTest):
|
||||
@@ -123,7 +131,7 @@ class TestEditorTest:
|
||||
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorTestSuite
|
||||
|
||||
@pytest.mark.SUITE_main
|
||||
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
|
||||
@pytest.mark.parametrize("launcher_platform", [{get_editor_launcher_platform()}])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
class TestAutomation(EditorTestSuite):
|
||||
{module_class_code}
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
|
||||
struct ICVar;
|
||||
|
||||
class CVarMenu
|
||||
: public QMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// CVar that can be toggled on and off
|
||||
struct CVarToggle
|
||||
|
||||
+87
-95
@@ -19,10 +19,9 @@ namespace Config
|
||||
|
||||
CConfigGroup::~CConfigGroup()
|
||||
{
|
||||
for (TConfigVariables::const_iterator it = m_vars.begin();
|
||||
it != m_vars.end(); ++it)
|
||||
for (IConfigVar* var : m_vars)
|
||||
{
|
||||
delete (*it);
|
||||
delete var;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,17 +30,15 @@ namespace Config
|
||||
m_vars.push_back(var);
|
||||
}
|
||||
|
||||
uint32 CConfigGroup::GetVarCount()
|
||||
AZ::u32 CConfigGroup::GetVarCount()
|
||||
{
|
||||
return static_cast<uint32>(m_vars.size());
|
||||
return aznumeric_cast<AZ::u32>(m_vars.size());
|
||||
}
|
||||
|
||||
IConfigVar* CConfigGroup::GetVar(const char* szName)
|
||||
{
|
||||
for (TConfigVariables::const_iterator it = m_vars.begin();
|
||||
it != m_vars.end(); ++it)
|
||||
for (IConfigVar* var : m_vars)
|
||||
{
|
||||
IConfigVar* var = (*it);
|
||||
if (0 == _stricmp(szName, var->GetName().c_str()))
|
||||
{
|
||||
return var;
|
||||
@@ -53,20 +50,19 @@ namespace Config
|
||||
|
||||
const IConfigVar* CConfigGroup::GetVar(const char* szName) const
|
||||
{
|
||||
for (TConfigVariables::const_iterator it = m_vars.begin();
|
||||
it != m_vars.end(); ++it)
|
||||
for (const IConfigVar* var : m_vars)
|
||||
{
|
||||
IConfigVar* var = (*it);
|
||||
if (0 == _stricmp(szName, var->GetName().c_str()))
|
||||
{
|
||||
return var;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IConfigVar* CConfigGroup::GetVar(uint index)
|
||||
IConfigVar* CConfigGroup::GetVar(AZ::u32 index)
|
||||
{
|
||||
if (index < m_vars.size())
|
||||
{
|
||||
@@ -76,7 +72,7 @@ namespace Config
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const IConfigVar* CConfigGroup::GetVar(uint index) const
|
||||
const IConfigVar* CConfigGroup::GetVar(AZ::u32 index) const
|
||||
{
|
||||
if (index < m_vars.size())
|
||||
{
|
||||
@@ -89,114 +85,110 @@ namespace Config
|
||||
void CConfigGroup::SaveToXML(XmlNodeRef node)
|
||||
{
|
||||
// save only values that don't have default values
|
||||
for (TConfigVariables::const_iterator it = m_vars.begin();
|
||||
it != m_vars.end(); ++it)
|
||||
for (const IConfigVar* var : m_vars)
|
||||
{
|
||||
IConfigVar* var = (*it);
|
||||
if (!var->IsFlagSet(IConfigVar::eFlag_DoNotSave))
|
||||
if (var->IsFlagSet(IConfigVar::eFlag_DoNotSave) || var->IsDefault())
|
||||
{
|
||||
if (!var->IsDefault())
|
||||
{
|
||||
const char* szName = var->GetName().c_str();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (var->GetType())
|
||||
{
|
||||
case IConfigVar::eType_BOOL:
|
||||
{
|
||||
bool currentValue = false;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue);
|
||||
break;
|
||||
}
|
||||
const char* szName = var->GetName().c_str();
|
||||
|
||||
case IConfigVar::eType_INT:
|
||||
{
|
||||
int currentValue = 0;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue);
|
||||
break;
|
||||
}
|
||||
switch (var->GetType())
|
||||
{
|
||||
case IConfigVar::eType_BOOL:
|
||||
{
|
||||
bool currentValue = false;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case IConfigVar::eType_FLOAT:
|
||||
{
|
||||
float currentValue = 0;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue);
|
||||
break;
|
||||
}
|
||||
case IConfigVar::eType_INT:
|
||||
{
|
||||
int currentValue = 0;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case IConfigVar::eType_STRING:
|
||||
{
|
||||
AZStd::string currentValue;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
case IConfigVar::eType_FLOAT:
|
||||
{
|
||||
float currentValue = 0;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case IConfigVar::eType_STRING:
|
||||
{
|
||||
AZStd::string currentValue;
|
||||
var->Get(¤tValue);
|
||||
node->setAttr(szName, currentValue.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CConfigGroup::LoadFromXML(XmlNodeRef node)
|
||||
{
|
||||
// save only values that don't have default values
|
||||
for (TConfigVariables::const_iterator it = m_vars.begin();
|
||||
it != m_vars.end(); ++it)
|
||||
// load values that are save-able
|
||||
for (IConfigVar* var : m_vars)
|
||||
{
|
||||
IConfigVar* var = (*it);
|
||||
if (!var->IsFlagSet(IConfigVar::eFlag_DoNotSave))
|
||||
if (var->IsFlagSet(IConfigVar::eFlag_DoNotSave))
|
||||
{
|
||||
const char* szName = var->GetName().c_str();
|
||||
continue;
|
||||
}
|
||||
const char* szName = var->GetName().c_str();
|
||||
|
||||
switch (var->GetType())
|
||||
switch (var->GetType())
|
||||
{
|
||||
case IConfigVar::eType_BOOL:
|
||||
{
|
||||
bool currentValue = false;
|
||||
var->GetDefault(¤tValue);
|
||||
if (node->getAttr(szName, currentValue))
|
||||
{
|
||||
case IConfigVar::eType_BOOL:
|
||||
{
|
||||
bool currentValue = false;
|
||||
var->GetDefault(¤tValue);
|
||||
if (node->getAttr(szName, currentValue))
|
||||
{
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IConfigVar::eType_INT:
|
||||
case IConfigVar::eType_INT:
|
||||
{
|
||||
int currentValue = 0;
|
||||
var->GetDefault(¤tValue);
|
||||
if (node->getAttr(szName, currentValue))
|
||||
{
|
||||
int currentValue = 0;
|
||||
var->GetDefault(¤tValue);
|
||||
if (node->getAttr(szName, currentValue))
|
||||
{
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IConfigVar::eType_FLOAT:
|
||||
case IConfigVar::eType_FLOAT:
|
||||
{
|
||||
float currentValue = 0;
|
||||
var->GetDefault(¤tValue);
|
||||
if (node->getAttr(szName, currentValue))
|
||||
{
|
||||
float currentValue = 0;
|
||||
var->GetDefault(¤tValue);
|
||||
if (node->getAttr(szName, currentValue))
|
||||
{
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IConfigVar::eType_STRING:
|
||||
case IConfigVar::eType_STRING:
|
||||
{
|
||||
AZStd::string currentValue;
|
||||
var->GetDefault(¤tValue);
|
||||
QString readValue(currentValue.c_str());
|
||||
if (node->getAttr(szName, readValue))
|
||||
{
|
||||
AZStd::string currentValue;
|
||||
var->GetDefault(¤tValue);
|
||||
QString readValue(currentValue.c_str());
|
||||
if (node->getAttr(szName, readValue))
|
||||
{
|
||||
currentValue = readValue.toUtf8().data();
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
currentValue = readValue.toUtf8().data();
|
||||
var->Set(¤tValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-69
@@ -8,8 +8,12 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef CRYINCLUDE_EDITOR_CONFIGGROUP_H
|
||||
#define CRYINCLUDE_EDITOR_CONFIGGROUP_H
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
struct ICVar;
|
||||
class XmlNodeRef;
|
||||
|
||||
namespace Config
|
||||
{
|
||||
@@ -32,7 +36,7 @@ namespace Config
|
||||
eFlag_DoNotSave = 1 << 2,
|
||||
};
|
||||
|
||||
IConfigVar(const char* szName, const char* szDescription, EType varType, uint8 flags)
|
||||
IConfigVar(const char* szName, const char* szDescription, EType varType, AZ::u8 flags)
|
||||
: m_name(szName)
|
||||
, m_description(szDescription)
|
||||
, m_type(varType)
|
||||
@@ -42,22 +46,22 @@ namespace Config
|
||||
|
||||
virtual ~IConfigVar() = default;
|
||||
|
||||
ILINE EType GetType() const
|
||||
AZ_FORCE_INLINE EType GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
ILINE const AZStd::string& GetName() const
|
||||
AZ_FORCE_INLINE const AZStd::string& GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
ILINE const AZStd::string& GetDescription() const
|
||||
AZ_FORCE_INLINE const AZStd::string& GetDescription() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
ILINE bool IsFlagSet(EFlags flag) const
|
||||
AZ_FORCE_INLINE bool IsFlagSet(EFlags flag) const
|
||||
{
|
||||
return 0 != (m_flags & flag);
|
||||
}
|
||||
@@ -68,73 +72,28 @@ namespace Config
|
||||
virtual void GetDefault(void* outPtr) const = 0;
|
||||
virtual void Reset() = 0;
|
||||
|
||||
static EType TranslateType(const bool&) { return eType_BOOL; }
|
||||
static EType TranslateType(const int&) { return eType_INT; }
|
||||
static EType TranslateType(const float&) { return eType_FLOAT; }
|
||||
static EType TranslateType(const AZStd::string&) { return eType_STRING; }
|
||||
static constexpr EType TranslateType(const bool&) { return eType_BOOL; }
|
||||
static constexpr EType TranslateType(const int&) { return eType_INT; }
|
||||
static constexpr EType TranslateType(const float&) { return eType_FLOAT; }
|
||||
static constexpr EType TranslateType(const AZStd::string&) { return eType_STRING; }
|
||||
|
||||
protected:
|
||||
EType m_type;
|
||||
uint8 m_flags;
|
||||
AZ::u8 m_flags;
|
||||
AZStd::string m_name;
|
||||
AZStd::string m_description;
|
||||
void* m_ptr;
|
||||
ICVar* m_pCVar;
|
||||
};
|
||||
|
||||
// Typed wrapper for config variable
|
||||
template<class T>
|
||||
class TConfigVar
|
||||
: public IConfigVar
|
||||
{
|
||||
private:
|
||||
T m_default;
|
||||
|
||||
public:
|
||||
TConfigVar(const char* szName, const char* szDescription, uint8 flags, T& ptr, const T& defaultValue)
|
||||
: IConfigVar(szName, szDescription, IConfigVar::TranslateType(ptr), flags)
|
||||
, m_default(defaultValue)
|
||||
{
|
||||
m_ptr = &ptr;
|
||||
|
||||
// reset to default value on initializations
|
||||
ptr = defaultValue;
|
||||
}
|
||||
|
||||
virtual void Get(void* outPtr) const
|
||||
{
|
||||
*reinterpret_cast<T*>(outPtr) = *reinterpret_cast<const T*>(m_ptr);
|
||||
}
|
||||
|
||||
virtual void Set(const void* ptr)
|
||||
{
|
||||
*reinterpret_cast<T*>(m_ptr) = *reinterpret_cast<const T*>(ptr);
|
||||
}
|
||||
|
||||
virtual void Reset()
|
||||
{
|
||||
*reinterpret_cast<T*>(m_ptr) = m_default;
|
||||
}
|
||||
|
||||
virtual void GetDefault(void* outPtr) const
|
||||
{
|
||||
*reinterpret_cast<T*>(outPtr) = m_default;
|
||||
}
|
||||
|
||||
virtual bool IsDefault() const
|
||||
{
|
||||
return *reinterpret_cast<const T*>(m_ptr) == m_default;
|
||||
}
|
||||
};
|
||||
|
||||
// Group of configuration variables with optional mapping to CVars
|
||||
class CConfigGroup
|
||||
{
|
||||
private:
|
||||
typedef std::vector<IConfigVar*> TConfigVariables;
|
||||
using TConfigVariables = AZStd::vector<IConfigVar*> ;
|
||||
TConfigVariables m_vars;
|
||||
|
||||
typedef std::vector<ICVar*> TConsoleVariables;
|
||||
using TConsoleVariables = AZStd::vector<ICVar*>;
|
||||
TConsoleVariables m_consoleVars;
|
||||
|
||||
public:
|
||||
@@ -142,20 +101,13 @@ namespace Config
|
||||
virtual ~CConfigGroup();
|
||||
|
||||
void AddVar(IConfigVar* var);
|
||||
uint32 GetVarCount();
|
||||
AZ::u32 GetVarCount();
|
||||
IConfigVar* GetVar(const char* szName);
|
||||
IConfigVar* GetVar(uint index);
|
||||
IConfigVar* GetVar(AZ::u32 index);
|
||||
const IConfigVar* GetVar(const char* szName) const;
|
||||
const IConfigVar* GetVar(uint index) const;
|
||||
const IConfigVar* GetVar(AZ::u32 index) const;
|
||||
|
||||
void SaveToXML(XmlNodeRef node);
|
||||
void LoadFromXML(XmlNodeRef node);
|
||||
|
||||
template<class T>
|
||||
void AddVar(const char* szName, const char* szDescription, T& var, const T& defaultValue, uint8 flags = 0)
|
||||
{
|
||||
AddVar(new TConfigVar<T>(szName, szDescription, flags, var, defaultValue));
|
||||
}
|
||||
};
|
||||
};
|
||||
#endif // CRYINCLUDE_EDITOR_CONFIGGROUP_H
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
|
||||
#define CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
@@ -53,6 +51,7 @@ private:
|
||||
|
||||
class UserPopupWidgetHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarUser, UserPropertyEditor>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(UserPopupWidgetHandler, AZ::SystemAllocator, 0);
|
||||
bool IsDefaultHandler() const override { return false; }
|
||||
@@ -67,6 +66,7 @@ public:
|
||||
|
||||
class FloatCurveHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarSpline, CSplineCtrl>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(FloatCurveHandler, AZ::SystemAllocator, 0);
|
||||
bool IsDefaultHandler() const override { return false; }
|
||||
@@ -80,5 +80,3 @@ public:
|
||||
|
||||
void OnSplineChange(CSplineCtrl*);
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
|
||||
|
||||
@@ -58,17 +58,9 @@ private:
|
||||
void OnClicked() override
|
||||
{
|
||||
QString tempValue("");
|
||||
QString ext("");
|
||||
if (m_path.isEmpty() == false)
|
||||
if (!m_path.isEmpty() && !Path::GetExt(m_path).isEmpty())
|
||||
{
|
||||
if (Path::GetExt(m_path) == "")
|
||||
{
|
||||
tempValue = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
tempValue = m_path;
|
||||
}
|
||||
tempValue = m_path;
|
||||
}
|
||||
|
||||
AssetSelectionModel selection;
|
||||
|
||||
@@ -99,6 +99,7 @@ class FileResourceSelectorWidgetHandler
|
||||
: QObject
|
||||
, public AzToolsFramework::PropertyHandler < CReflectedVarResource, FileResourceSelectorWidget >
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(FileResourceSelectorWidgetHandler, AZ::SystemAllocator, 0);
|
||||
|
||||
|
||||
@@ -446,41 +446,54 @@ void ReflectedVarUserAdapter::SetVariable(IVariable *pVariable)
|
||||
m_reflectedVar.reset(new CReflectedVarUser( pVariable->GetHumanName().toUtf8().data()));
|
||||
}
|
||||
|
||||
void ReflectedVarUserAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
|
||||
void ReflectedVarUserAdapter::SyncReflectedVarToIVar(IVariable* pVariable)
|
||||
{
|
||||
QString value;
|
||||
pVariable->Get(value);
|
||||
m_reflectedVar->m_value = value.toUtf8().data();
|
||||
|
||||
//extract the list of custom items from the IVariable user data
|
||||
IVariable::IGetCustomItems* pGetCustomItems = static_cast<IVariable::IGetCustomItems*> (pVariable->GetUserData().value<void *>());
|
||||
if (pGetCustomItems != nullptr)
|
||||
{
|
||||
std::vector<IVariable::IGetCustomItems::SItem> items;
|
||||
QString dlgTitle;
|
||||
// call the user supplied callback to fill-in items and get dialog title
|
||||
bool bShowIt = pGetCustomItems->GetItems(pVariable, items, dlgTitle);
|
||||
if (bShowIt) // if func didn't veto, show the dialog
|
||||
{
|
||||
m_reflectedVar->m_enableEdit = true;
|
||||
m_reflectedVar->m_useTree = pGetCustomItems->UseTree();
|
||||
m_reflectedVar->m_treeSeparator = pGetCustomItems->GetTreeSeparator();
|
||||
m_reflectedVar->m_dialogTitle = dlgTitle.toUtf8().data();
|
||||
m_reflectedVar->m_itemNames.resize(items.size());
|
||||
m_reflectedVar->m_itemDescriptions.resize(items.size());
|
||||
|
||||
QByteArray ba;
|
||||
int i = -1;
|
||||
std::generate(m_reflectedVar->m_itemNames.begin(), m_reflectedVar->m_itemNames.end(), [&items, &i, &ba]() { ++i; ba = items[i].name.toUtf8(); return ba.data(); });
|
||||
i = -1;
|
||||
std::generate(m_reflectedVar->m_itemDescriptions.begin(), m_reflectedVar->m_itemDescriptions.end(), [&items, &i, &ba]() { ++i; ba = items[i].desc.toUtf8(); return ba.data(); });
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
// extract the list of custom items from the IVariable user data
|
||||
IVariable::IGetCustomItems* pGetCustomItems = static_cast<IVariable::IGetCustomItems*>(pVariable->GetUserData().value<void*>());
|
||||
if (pGetCustomItems == nullptr)
|
||||
{
|
||||
m_reflectedVar->m_enableEdit = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<IVariable::IGetCustomItems::SItem> items;
|
||||
QString dlgTitle;
|
||||
// call the user supplied callback to fill-in items and get dialog title
|
||||
bool bShowIt = pGetCustomItems->GetItems(pVariable, items, dlgTitle);
|
||||
if (!bShowIt) // if func vetoed it, don't show the dialog
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_reflectedVar->m_enableEdit = true;
|
||||
m_reflectedVar->m_useTree = pGetCustomItems->UseTree();
|
||||
m_reflectedVar->m_treeSeparator = pGetCustomItems->GetTreeSeparator();
|
||||
m_reflectedVar->m_dialogTitle = dlgTitle.toUtf8().data();
|
||||
m_reflectedVar->m_itemNames.resize(items.size());
|
||||
m_reflectedVar->m_itemDescriptions.resize(items.size());
|
||||
|
||||
QByteArray ba;
|
||||
int i = -1;
|
||||
AZStd::generate(
|
||||
m_reflectedVar->m_itemNames.begin(), m_reflectedVar->m_itemNames.end(),
|
||||
[&items, &i, &ba]()
|
||||
{
|
||||
++i;
|
||||
ba = items[i].name.toUtf8();
|
||||
return ba.data();
|
||||
});
|
||||
i = -1;
|
||||
AZStd::generate(
|
||||
m_reflectedVar->m_itemDescriptions.begin(), m_reflectedVar->m_itemDescriptions.end(),
|
||||
[&items, &i, &ba]()
|
||||
{
|
||||
++i;
|
||||
ba = items[i].desc.toUtf8();
|
||||
return ba.data();
|
||||
});
|
||||
}
|
||||
|
||||
void ReflectedVarUserAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
|
||||
|
||||
@@ -126,7 +126,6 @@ void TimelineWidget::DrawTicks(QPainter* painter)
|
||||
const QPen pOldPen = painter->pen();
|
||||
|
||||
const QPen ltgray(QColor(110, 110, 110));
|
||||
const QPen black(palette().color(QPalette::Normal, QPalette::Text));
|
||||
const QPen redpen(QColor(255, 0, 255));
|
||||
|
||||
// Draw time ticks every tick step seconds.
|
||||
@@ -598,7 +597,6 @@ void TimelineWidget::DrawSecondTicks(QPainter* painter)
|
||||
{
|
||||
const QPen ltgray(QColor(110, 110, 110));
|
||||
const QPen black(palette().color(QPalette::Normal, QPalette::Text));
|
||||
const QPen redpen(QColor(255, 0, 255));
|
||||
|
||||
for (int gx = m_grid.firstGridLine.x(); gx < m_grid.firstGridLine.x() + m_grid.numGridLines.x() + 1; gx++)
|
||||
{
|
||||
|
||||
@@ -4137,7 +4137,15 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
|
||||
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
|
||||
Editor::EditorQtApplication* app = Editor::EditorQtApplication::newInstance(argc, argv);
|
||||
|
||||
if (app->arguments().contains("-autotest_mode"))
|
||||
QStringList qArgs = app->arguments();
|
||||
const bool is_automated_test = AZStd::any_of(qArgs.begin(), qArgs.end(),
|
||||
[](const QString& elem)
|
||||
{
|
||||
return elem.endsWith("autotest_mode") || elem.endsWith("runpythontest");
|
||||
}
|
||||
);
|
||||
|
||||
if (is_automated_test)
|
||||
{
|
||||
// Nullroute all stdout to null for automated tests, this way we make sure
|
||||
// that the test result output is not polluted with unrelated output data.
|
||||
|
||||
@@ -431,6 +431,7 @@ public:
|
||||
class CCrySingleDocTemplate
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
explicit CCrySingleDocTemplate(const QMetaObject* pDocClass)
|
||||
: QObject()
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
// Notice : Refer to ViewportTitleDlg.cpp for a use case.
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H
|
||||
#define CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
@@ -28,6 +26,7 @@ namespace Ui
|
||||
class CCustomResolutionDlg
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CCustomResolutionDlg(int w, int h, QWidget* pParent = nullptr);
|
||||
~CCustomResolutionDlg();
|
||||
@@ -42,5 +41,3 @@ protected:
|
||||
|
||||
QScopedPointer<Ui::CustomResolutionDlg> m_ui;
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_CUSTOMRESOLUTIONDLG_H
|
||||
|
||||
@@ -211,9 +211,9 @@ public:
|
||||
|
||||
void Reset(QAction& action)
|
||||
{
|
||||
emit beginResetModel();
|
||||
beginResetModel();
|
||||
m_action = &action;
|
||||
emit endResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -266,7 +266,7 @@ QStringList CustomizeKeyboardDialog::BuildModels(QWidget* parent)
|
||||
categories.append(category);
|
||||
|
||||
QMenu* menu = menuAction->menu();
|
||||
m_menuActions[category] = GetAllActionsForMenu(menu, QStringLiteral(""));
|
||||
m_menuActions[category] = GetAllActionsForMenu(menu, QString());
|
||||
}
|
||||
|
||||
return categories;
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace SandboxEditor
|
||||
connect(m_ui->okButton, &QPushButton::clicked, this, &ErrorDialog::OnOK);
|
||||
connect(
|
||||
m_ui->messages,
|
||||
SIGNAL(itemSelectionChanged()),
|
||||
&QTreeWidget::itemSelectionChanged,
|
||||
this,
|
||||
SLOT(MessageSelectionChanged()));
|
||||
&ErrorDialog::MessageSelectionChanged);
|
||||
}
|
||||
|
||||
ErrorDialog::~ErrorDialog()
|
||||
|
||||
@@ -240,7 +240,7 @@ QJsonObject KeyboardCustomizationSettings::ExportGroup()
|
||||
|
||||
void KeyboardCustomizationSettings::ImportFromFile(QWidget* parent)
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(parent, QObject::tr("Export Keyboard Shortcuts"), QStringLiteral(""), QObject::tr("Keyboard Settings (*.keys)"));
|
||||
QString fileName = QFileDialog::getOpenFileName(parent, QObject::tr("Export Keyboard Shortcuts"), QString(), QObject::tr("Keyboard Settings (*.keys)"));
|
||||
if (fileName.isEmpty())
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -419,7 +419,7 @@ void MemoryStatusItem::updateStatus()
|
||||
GeneralStatusItem::GeneralStatusItem(QString name, MainStatusBar* parent)
|
||||
: StatusBarItem(name, parent)
|
||||
{
|
||||
connect(parent, SIGNAL(messageChanged(QString)), this, SLOT(update()));
|
||||
connect(parent, &MainStatusBar::messageChanged, this, [this](const QString&) { update(); });
|
||||
}
|
||||
|
||||
QString GeneralStatusItem::CurrentText() const
|
||||
|
||||
@@ -100,9 +100,10 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/)
|
||||
m_level = "";
|
||||
// First of all, keyboard focus is related to widget tab order, and the default tab order is based on the order in which
|
||||
// widgets are constructed. Therefore, creating more widgets changes the keyboard focus. That is why setFocus() is called last.
|
||||
// Secondly, using singleShot() allows setFocus() slot of the QLineEdit instance to be invoked right after the event system
|
||||
// is ready to do so. Therefore, it is better to use singleShot() than directly call setFocus().
|
||||
QTimer::singleShot(0, ui->LEVEL, SLOT(OnStartup()));
|
||||
// in OnStartup()
|
||||
// Secondly, using singleShot() allows OnStartup() slot of the QLineEdit instance to be invoked right after the event system
|
||||
// is ready to do so. Therefore, it is better to use singleShot() than directly call OnStartup().
|
||||
QTimer::singleShot(0, this, &CNewLevelDialog::OnStartup);
|
||||
|
||||
ReloadLevelFolder();
|
||||
}
|
||||
|
||||
@@ -168,6 +168,11 @@ namespace AZ
|
||||
|
||||
//! Rotation modifiers
|
||||
//! @{
|
||||
//! Set the world rotation matrix using the composition of rotations around
|
||||
//! the principle axes in the order of z-axis first and y-axis and then x-axis.
|
||||
//! @param eulerRadianAngles A Vector3 denoting radian angles of the rotations around each principle axis.
|
||||
virtual void SetWorldRotation([[maybe_unused]] const AZ::Vector3& eulerAnglesRadian) {}
|
||||
|
||||
//! Sets the entity's rotation in the world in quaternion notation.
|
||||
//! The origin of the axes is the entity's position in world space.
|
||||
//! @param quaternion A quaternion that represents the rotation to use for the entity.
|
||||
|
||||
@@ -323,6 +323,13 @@ namespace AzFramework
|
||||
return localZ;
|
||||
}
|
||||
|
||||
void TransformComponent::SetWorldRotation(const AZ::Vector3& eulerAnglesRadian)
|
||||
{
|
||||
AZ::Transform newWorldTransform = m_worldTM;
|
||||
newWorldTransform.SetRotation(AZ::Quaternion::CreateFromEulerAnglesRadians(eulerAnglesRadian));
|
||||
SetWorldTM(newWorldTransform);
|
||||
}
|
||||
|
||||
void TransformComponent::SetWorldRotationQuaternion(const AZ::Quaternion& quaternion)
|
||||
{
|
||||
AZ::Transform newWorldTransform = m_worldTM;
|
||||
|
||||
@@ -108,6 +108,7 @@ namespace AzFramework
|
||||
float GetLocalZ() override;
|
||||
|
||||
// Rotation modifiers
|
||||
void SetWorldRotation(const AZ::Vector3& eulerAnglesRadian) override;
|
||||
void SetWorldRotationQuaternion(const AZ::Quaternion& quaternion) override;
|
||||
|
||||
AZ::Vector3 GetWorldRotation() override;
|
||||
|
||||
+67
-1
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
namespace AzPhysics
|
||||
{
|
||||
@@ -28,6 +29,71 @@ namespace AzPhysics
|
||||
->Field("ChildLocalPosition", &JointConfiguration::m_childLocalPosition)
|
||||
->Field("StartSimulationEnabled", &JointConfiguration::m_startSimulationEnabled)
|
||||
;
|
||||
|
||||
if (auto* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<JointConfiguration>("Joint Configuration", "Joint configuration.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalRotation,
|
||||
"Parent local rotation", "Parent joint frame relative to parent body.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetParentLocalRotationVisibility)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_parentLocalPosition,
|
||||
"Parent local position", "Joint position relative to parent body.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetParentLocalPositionVisibility)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalRotation,
|
||||
"Child local rotation", "Child joint frame relative to child body.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetChildLocalRotationVisibility)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_childLocalPosition,
|
||||
"Child local position", "Joint position relative to child body.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetChildLocalPositionVisibility)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &JointConfiguration::m_startSimulationEnabled,
|
||||
"Start simulation enabled", "When active, the joint will be enabled when the simulation begins.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &JointConfiguration::GetStartSimulationEnabledVisibility)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Crc32 JointConfiguration::GetPropertyVisibility(JointConfiguration::PropertyVisibility property) const
|
||||
{
|
||||
return (m_propertyVisibilityFlags & property) != 0 ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
|
||||
}
|
||||
|
||||
void JointConfiguration::SetPropertyVisibility(JointConfiguration::PropertyVisibility property, bool isVisible)
|
||||
{
|
||||
if (isVisible)
|
||||
{
|
||||
m_propertyVisibilityFlags |= property;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_propertyVisibilityFlags &= ~property;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Crc32 JointConfiguration::GetParentLocalRotationVisibility() const
|
||||
{
|
||||
return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ParentLocalRotation);
|
||||
}
|
||||
|
||||
AZ::Crc32 JointConfiguration::GetParentLocalPositionVisibility() const
|
||||
{
|
||||
return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ParentLocalPosition);
|
||||
}
|
||||
|
||||
AZ::Crc32 JointConfiguration::GetChildLocalRotationVisibility() const
|
||||
{
|
||||
return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ChildLocalRotation);
|
||||
}
|
||||
|
||||
AZ::Crc32 JointConfiguration::GetChildLocalPositionVisibility() const
|
||||
{
|
||||
return GetPropertyVisibility(JointConfiguration::PropertyVisibility::ChildLocalPosition);
|
||||
}
|
||||
|
||||
AZ::Crc32 JointConfiguration::GetStartSimulationEnabledVisibility() const
|
||||
{
|
||||
return GetPropertyVisibility(JointConfiguration::PropertyVisibility::StartSimulationEnabled);
|
||||
}
|
||||
} // namespace AzPhysics
|
||||
|
||||
@@ -31,6 +31,25 @@ namespace AzPhysics
|
||||
JointConfiguration() = default;
|
||||
virtual ~JointConfiguration() = default;
|
||||
|
||||
// Visibility helpers for use in the Editor when reflected.
|
||||
enum PropertyVisibility : AZ::u8
|
||||
{
|
||||
ParentLocalRotation = 1 << 0, //!< Whether the parent local rotation is visible.
|
||||
ParentLocalPosition = 1 << 1, //!< Whether the parent local position is visible.
|
||||
ChildLocalRotation = 1 << 2, //!< Whether the child local rotation is visible.
|
||||
ChildLocalPosition = 1 << 3, //!< Whether the child local position is visible.
|
||||
StartSimulationEnabled = 1 << 4 //!< Whether the start simulation enabled setting is visible.
|
||||
};
|
||||
|
||||
AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const;
|
||||
void SetPropertyVisibility(PropertyVisibility property, bool isVisible);
|
||||
|
||||
AZ::Crc32 GetParentLocalRotationVisibility() const;
|
||||
AZ::Crc32 GetParentLocalPositionVisibility() const;
|
||||
AZ::Crc32 GetChildLocalRotationVisibility() const;
|
||||
AZ::Crc32 GetChildLocalPositionVisibility() const;
|
||||
AZ::Crc32 GetStartSimulationEnabledVisibility() const;
|
||||
|
||||
// Entity/object association.
|
||||
void* m_customUserData = nullptr;
|
||||
|
||||
@@ -40,8 +59,11 @@ namespace AzPhysics
|
||||
AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body.
|
||||
AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body.
|
||||
bool m_startSimulationEnabled = true;
|
||||
|
||||
|
||||
// For debugging/tracking purposes only.
|
||||
AZStd::string m_debugName;
|
||||
|
||||
// Default all visibility settings to invisible, since most joint configurations don't need to display these.
|
||||
AZ::u8 m_propertyVisibilityFlags = 0;
|
||||
};
|
||||
}
|
||||
|
||||
+11
-3
@@ -1387,7 +1387,10 @@ namespace AzToolsFramework
|
||||
QDataStream stream(&pixmapBytes, QIODevice::ReadOnly);
|
||||
QPixmap pixmap;
|
||||
stream >> pixmap;
|
||||
GUI->SetBrowseButtonIcon(pixmap);
|
||||
if (!pixmap.isNull())
|
||||
{
|
||||
GUI->SetBrowseButtonIcon(pixmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1417,6 +1420,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
else if (attrib == AZ_CRC_CE("ThumbnailIcon"))
|
||||
{
|
||||
GUI->SetCustomThumbnailEnabled(false);
|
||||
|
||||
AZStd::string iconPath;
|
||||
if (attrValue->Read<AZStd::string>(iconPath) && !iconPath.empty())
|
||||
{
|
||||
@@ -1434,8 +1439,11 @@ namespace AzToolsFramework
|
||||
QDataStream stream(&pixmapBytes, QIODevice::ReadOnly);
|
||||
QPixmap pixmap;
|
||||
stream >> pixmap;
|
||||
GUI->SetCustomThumbnailEnabled(true);
|
||||
GUI->SetCustomThumbnailPixmap(pixmap);
|
||||
if (!pixmap.isNull())
|
||||
{
|
||||
GUI->SetCustomThumbnailEnabled(true);
|
||||
GUI->SetCustomThumbnailPixmap(pixmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-10
@@ -67,16 +67,9 @@ namespace AzToolsFramework
|
||||
|
||||
void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName)
|
||||
{
|
||||
if (m_customThumbnailEnabled)
|
||||
{
|
||||
ClearThumbnail();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_key = key;
|
||||
m_thumbnail->SetThumbnailKey(m_key, contextName);
|
||||
m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName);
|
||||
}
|
||||
m_key = key;
|
||||
m_thumbnail->SetThumbnailKey(m_key, contextName);
|
||||
m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName);
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
HighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin);
|
||||
m_componentModeBorderText.setVisible(true);
|
||||
m_componentModeBorderText.setText(borderTitle.c_str());
|
||||
UpdateUiOverlayGeometry();
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::RemoveViewportBorder()
|
||||
|
||||
@@ -115,17 +115,20 @@ CViewSystem::CViewSystem(ISystem* pSystem)
|
||||
, m_useDeferredViewSystemUpdate(false)
|
||||
, m_bControlsAudioListeners(true)
|
||||
{
|
||||
#if !defined(_RELEASE) && !defined(DEDICATED_SERVER)
|
||||
if (!s_debugCamera)
|
||||
#if !defined(_RELEASE)
|
||||
if (!gEnv->IsDedicated())
|
||||
{
|
||||
s_debugCamera = new DebugCamera;
|
||||
}
|
||||
if (!s_debugCamera)
|
||||
{
|
||||
s_debugCamera = new DebugCamera;
|
||||
}
|
||||
|
||||
REGISTER_COMMAND("debugCameraToggle", ToggleDebugCamera, VF_DEV_ONLY, "Toggle the debug camera.\n");
|
||||
REGISTER_COMMAND("debugCameraInvertY", ToggleDebugCameraInvertY, VF_DEV_ONLY, "Toggle debug camera Y-axis inversion.\n");
|
||||
REGISTER_COMMAND("debugCameraMove", DebugCameraMove, VF_DEV_ONLY, "Move the debug camera the specified distance (x y z).\n");
|
||||
gEnv->pConsole->CreateKeyBind("ctrl_keyboard_key_punctuation_backslash", "debugCameraToggle");
|
||||
gEnv->pConsole->CreateKeyBind("alt_keyboard_key_punctuation_backslash", "debugCameraInvertY");
|
||||
REGISTER_COMMAND("debugCameraToggle", ToggleDebugCamera, VF_DEV_ONLY, "Toggle the debug camera.\n");
|
||||
REGISTER_COMMAND("debugCameraInvertY", ToggleDebugCameraInvertY, VF_DEV_ONLY, "Toggle debug camera Y-axis inversion.\n");
|
||||
REGISTER_COMMAND("debugCameraMove", DebugCameraMove, VF_DEV_ONLY, "Move the debug camera the specified distance (x y z).\n");
|
||||
gEnv->pConsole->CreateKeyBind("ctrl_keyboard_key_punctuation_backslash", "debugCameraToggle");
|
||||
gEnv->pConsole->CreateKeyBind("alt_keyboard_key_punctuation_backslash", "debugCameraInvertY");
|
||||
}
|
||||
#endif
|
||||
|
||||
REGISTER_CVAR2("cl_camera_noise", &m_fCameraNoise, -1, 0,
|
||||
@@ -167,6 +170,21 @@ CViewSystem::~CViewSystem()
|
||||
{
|
||||
m_pSystem->GetILevelSystem()->RemoveListener(this);
|
||||
}
|
||||
|
||||
#if !defined(_RELEASE)
|
||||
if (!gEnv->IsDedicated())
|
||||
{
|
||||
UNREGISTER_COMMAND("debugCameraToggle");
|
||||
UNREGISTER_COMMAND("debugCameraInvertY");
|
||||
UNREGISTER_COMMAND("debugCameraMove");
|
||||
|
||||
if (s_debugCamera)
|
||||
{
|
||||
delete s_debugCamera;
|
||||
s_debugCamera = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
@@ -47,6 +47,10 @@ ly_add_target(
|
||||
AZ::AssetBundlerBatch.Static
|
||||
)
|
||||
|
||||
# Adds a specialized .setreg to identify gems enabled in the active project.
|
||||
# This associates the AssetBundlerBatch target with the .Builders gem variants.
|
||||
ly_set_gem_variant_to_load(TARGETS AssetBundlerBatch VARIANTS Builders)
|
||||
|
||||
# AssetBundler - Qt GUI Application
|
||||
ly_add_target(
|
||||
NAME AssetBundler ${PAL_TRAIT_BUILD_ASSETBUNDLER_APPLICATION_TYPE}
|
||||
@@ -73,6 +77,10 @@ ly_add_target(
|
||||
${additional_dependencies}
|
||||
)
|
||||
|
||||
# Adds a specialized .setreg to identify gems enabled in the active project.
|
||||
# This associates the AssetBundler target with the .Builders gem variants.
|
||||
ly_set_gem_variant_to_load(TARGETS AssetBundler VARIANTS Builders)
|
||||
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
|
||||
ly_add_target(
|
||||
|
||||
@@ -54,7 +54,12 @@ namespace AssetBundler
|
||||
bool ApplicationManager::Init()
|
||||
{
|
||||
AZ::Debug::TraceMessageBus::Handler::BusConnect();
|
||||
Start(AzFramework::Application::Descriptor());
|
||||
|
||||
ComponentApplication::StartupParameters startupParameters;
|
||||
// The AssetBundler does not need to load gems
|
||||
startupParameters.m_loadDynamicModules = false;
|
||||
Start(AzFramework::Application::Descriptor(), startupParameters);
|
||||
|
||||
AZ::SerializeContext* context;
|
||||
EBUS_EVENT_RESULT(context, AZ::ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(context, "No serialize context");
|
||||
|
||||
@@ -71,7 +71,11 @@ namespace AssetBundler
|
||||
|
||||
|
||||
m_data->m_applicationManager.reset(aznew MockApplicationManagerTest(0, 0));
|
||||
m_data->m_applicationManager->Start(AzFramework::Application::Descriptor());
|
||||
|
||||
AZ::ComponentApplication::StartupParameters startupParameters;
|
||||
// The AssetBundler does not need to load gems
|
||||
startupParameters.m_loadDynamicModules = false;
|
||||
m_data->m_applicationManager->Start(AzFramework::Application::Descriptor(), startupParameters);
|
||||
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
|
||||
@@ -4447,7 +4447,9 @@ AssetBuilderSDK::AssetBuilderDesc MockBuilderInfoHandler::CreateBuilderDesc(cons
|
||||
|
||||
void FingerprintTest::SetUp()
|
||||
{
|
||||
AZ_Printf("FingerprintTest", "SetUp start");
|
||||
AssetProcessorManagerTest::SetUp();
|
||||
AZ_Printf("FingerprintTest", "SetUp self");
|
||||
|
||||
// We don't want the mock application manager to provide builder descriptors, mockBuilderInfoHandler will provide our own
|
||||
m_mockApplicationManager->BusDisconnect();
|
||||
@@ -4466,18 +4468,23 @@ void FingerprintTest::SetUp()
|
||||
});
|
||||
|
||||
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(m_absolutePath, ""));
|
||||
AZ_Printf("FingerprintTest", "SetUp end");
|
||||
}
|
||||
|
||||
void FingerprintTest::TearDown()
|
||||
{
|
||||
AZ_Printf("FingerprintTest", "TearDown start");
|
||||
m_jobResults = AZStd::vector<AssetProcessor::JobDetails>{};
|
||||
m_mockBuilderInfoHandler = {};
|
||||
|
||||
AZ_Printf("FingerprintTest", "TearDown parent");
|
||||
AssetProcessorManagerTest::TearDown();
|
||||
AZ_Printf("FingerprintTest", "TearDown end");
|
||||
}
|
||||
|
||||
void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString jobFingerprint, bool expectedResult)
|
||||
{
|
||||
AZ_Printf("FingerprintTest", "Fingerprint Test Start");
|
||||
m_mockBuilderInfoHandler.m_builderDesc.m_analysisFingerprint = builderFingerprint.toUtf8().data();
|
||||
m_mockBuilderInfoHandler.m_jobFingerprint = jobFingerprint;
|
||||
QMetaObject::invokeMethod(m_assetProcessorManager.get(), "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, m_absolutePath));
|
||||
@@ -4486,6 +4493,7 @@ void FingerprintTest::RunFingerprintTest(QString builderFingerprint, QString job
|
||||
ASSERT_EQ(m_mockBuilderInfoHandler.m_createJobsCount, 1);
|
||||
ASSERT_EQ(m_jobResults.size(), 1);
|
||||
ASSERT_EQ(m_jobResults[0].m_autoFail, expectedResult);
|
||||
AZ_Printf("FingerprintTest", "Fingerprint Test End");
|
||||
}
|
||||
|
||||
TEST_F(FingerprintTest, FingerprintChecking_JobFingerprint_NoBuilderFingerprint)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <AzCore/UnitTest/UnitTest.h>
|
||||
|
||||
//! These macros can be used for checking your unit tests,
|
||||
//! you can check AssetScannerUnitTest.cpp for usage
|
||||
@@ -155,6 +156,7 @@ namespace UnitTestUtils
|
||||
|
||||
bool OnPreWarning([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
++m_numWarningsAbsorbed;
|
||||
if (m_debugMessages)
|
||||
{
|
||||
@@ -165,6 +167,7 @@ namespace UnitTestUtils
|
||||
|
||||
bool OnPreAssert([[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
++m_numAssertsAbsorbed;
|
||||
if (m_debugMessages)
|
||||
{
|
||||
@@ -175,6 +178,7 @@ namespace UnitTestUtils
|
||||
|
||||
bool OnPreError([[maybe_unused]] const char* window, [[maybe_unused]] const char* fileName, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* message) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
++m_numErrorsAbsorbed;
|
||||
if (m_debugMessages)
|
||||
{
|
||||
@@ -183,8 +187,9 @@ namespace UnitTestUtils
|
||||
return true; // I handled this, do not forward it
|
||||
}
|
||||
|
||||
bool OnPrintf(const char* /*window*/, const char* /*message*/) override
|
||||
bool OnPrintf(const char* /*window*/, const char* message) override
|
||||
{
|
||||
UnitTest::ColoredPrintf(UnitTest::COLOR_YELLOW, message);
|
||||
++m_numMessagesAbsorbed;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ namespace O3DE::ProjectManager
|
||||
return AZ::Success(QStringList{ ProjectCMakeCommand,
|
||||
"-B", targetBuildPath,
|
||||
"-S", m_projectInfo.m_path,
|
||||
QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath),
|
||||
"-DLY_UNITY_BUILD=ON" } );
|
||||
QString("-DLY_3RDPARTY_PATH=").append(thirdPartyPath) } );
|
||||
}
|
||||
|
||||
AZ::Outcome<QStringList, QString> ProjectBuilderWorker::ConstructCmakeBuildCommandArguments() const
|
||||
|
||||
@@ -242,11 +242,15 @@ QTabBar::tab:focus {
|
||||
|
||||
/************** Project Settings **************/
|
||||
#projectSettings {
|
||||
margin-top:42px;
|
||||
margin-top:30px;
|
||||
}
|
||||
|
||||
#projectPreviewLabel {
|
||||
margin: 10px 0 5px 0;
|
||||
}
|
||||
|
||||
#projectTemplate {
|
||||
margin: 55px 0 0 50px;
|
||||
margin: 25px 0 0 50px;
|
||||
}
|
||||
#projectTemplateLabel {
|
||||
font-size:16px;
|
||||
|
||||
@@ -11,19 +11,28 @@
|
||||
#include <FormFolderBrowseEditWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <PathValidator.h>
|
||||
#include <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollArea>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
EngineSettingsScreen::EngineSettingsScreen(QWidget* parent)
|
||||
: ScreenWidget(parent)
|
||||
{
|
||||
auto* layout = new QVBoxLayout();
|
||||
QScrollArea* scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
|
||||
QWidget* scrollWidget = new QWidget(this);
|
||||
scrollArea->setWidget(scrollWidget);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(scrollWidget);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
scrollWidget->setLayout(layout);
|
||||
|
||||
setObjectName("engineSettingsScreen");
|
||||
|
||||
@@ -39,9 +48,18 @@ namespace O3DE::ProjectManager
|
||||
formTitleLabel->setObjectName("formTitleLabel");
|
||||
layout->addWidget(formTitleLabel);
|
||||
|
||||
m_engineVersion = new FormLineEditWidget(tr("Engine Version"), engineInfo.m_version, this);
|
||||
m_engineVersion->lineEdit()->setReadOnly(true);
|
||||
layout->addWidget(m_engineVersion);
|
||||
FormLineEditWidget* engineName = new FormLineEditWidget(tr("Engine Name"), engineInfo.m_name, this);
|
||||
engineName->lineEdit()->setReadOnly(true);
|
||||
layout->addWidget(engineName);
|
||||
|
||||
FormLineEditWidget* engineVersion = new FormLineEditWidget(tr("Engine Version"), engineInfo.m_version, this);
|
||||
engineVersion->lineEdit()->setReadOnly(true);
|
||||
layout->addWidget(engineVersion);
|
||||
|
||||
FormBrowseEditWidget* engineFolder = new FormBrowseEditWidget(tr("Engine Folder"), engineInfo.m_path, this);
|
||||
engineFolder->lineEdit()->setReadOnly(true);
|
||||
connect( engineFolder, &FormBrowseEditWidget::OnBrowse, [engineInfo]{ AzQtComponents::ShowFileOnDesktop(engineInfo.m_path); });
|
||||
layout->addWidget(engineFolder);
|
||||
|
||||
m_thirdParty = new FormFolderBrowseEditWidget(tr("3rd Party Software Folder"), engineInfo.m_thirdPartyPath, this);
|
||||
m_thirdParty->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
|
||||
@@ -71,7 +89,11 @@ namespace O3DE::ProjectManager
|
||||
connect(m_defaultProjectTemplates->lineEdit(), &QLineEdit::textChanged, this, &EngineSettingsScreen::OnTextChanged);
|
||||
layout->addWidget(m_defaultProjectTemplates);
|
||||
|
||||
setLayout(layout);
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout();
|
||||
mainLayout->setAlignment(Qt::AlignTop);
|
||||
mainLayout->setMargin(0);
|
||||
mainLayout->addWidget(scrollArea);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
ProjectManagerScreen EngineSettingsScreen::GetScreenEnum()
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace O3DE::ProjectManager
|
||||
void OnTextChanged();
|
||||
|
||||
private:
|
||||
FormLineEditWidget* m_engineVersion;
|
||||
FormBrowseEditWidget* m_thirdParty;
|
||||
FormBrowseEditWidget* m_defaultProjects;
|
||||
FormBrowseEditWidget* m_defaultGems;
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace O3DE::ProjectManager
|
||||
setObjectName("formBrowseEditWidget");
|
||||
|
||||
QPushButton* browseButton = new QPushButton(this);
|
||||
connect(browseButton, &QPushButton::pressed, this, &FormBrowseEditWidget::HandleBrowseButton);
|
||||
connect( browseButton, &QPushButton::pressed, [this]{ emit OnBrowse(); });
|
||||
connect( this, &FormBrowseEditWidget::OnBrowse, this, &FormBrowseEditWidget::HandleBrowseButton);
|
||||
m_frameLayout->addWidget(browseButton);
|
||||
}
|
||||
|
||||
@@ -34,7 +35,7 @@ namespace O3DE::ProjectManager
|
||||
int key = event->key();
|
||||
if (key == Qt::Key_Return || key == Qt::Key_Enter)
|
||||
{
|
||||
HandleBrowseButton();
|
||||
emit OnBrowse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,13 @@ namespace O3DE::ProjectManager
|
||||
explicit FormBrowseEditWidget(const QString& labelText = "", QWidget* parent = nullptr);
|
||||
~FormBrowseEditWidget() = default;
|
||||
|
||||
signals:
|
||||
void OnBrowse();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
protected slots:
|
||||
virtual void HandleBrowseButton() = 0;
|
||||
virtual void HandleBrowseButton() {};
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemCatalogScreen::ReinitForProject(const QString& projectPath)
|
||||
{
|
||||
m_gemModel->clear();
|
||||
m_gemModel->Clear();
|
||||
m_gemsToRegisterWithProject.clear();
|
||||
FillModel(projectPath);
|
||||
|
||||
@@ -145,10 +145,11 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies)
|
||||
void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies)
|
||||
{
|
||||
if (m_notificationsEnabled)
|
||||
{
|
||||
QModelIndex modelIndex = m_gemModel->FindIndexByNameString(gemName);
|
||||
bool added = GemModel::IsAdded(modelIndex);
|
||||
bool dependency = GemModel::IsAddedDependency(modelIndex);
|
||||
|
||||
@@ -233,7 +234,11 @@ namespace O3DE::ProjectManager
|
||||
const QVector<GemInfo> allRepoGemInfos = allRepoGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allRepoGemInfos)
|
||||
{
|
||||
m_gemModel->AddGem(gemInfo);
|
||||
// do not add gems that have already been downloaded
|
||||
if (!m_gemModel->FindIndexByNameString(gemInfo.m_name).isValid())
|
||||
{
|
||||
m_gemModel->AddGem(gemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -257,7 +262,8 @@ namespace O3DE::ProjectManager
|
||||
GemModel::SetWasPreviouslyAdded(*m_gemModel, modelIndex, true);
|
||||
GemModel::SetIsAdded(*m_gemModel, modelIndex, true);
|
||||
}
|
||||
else
|
||||
// ${Name} is a special name used in templates and is not really an error
|
||||
else if (enabledGemName != "${Name}")
|
||||
{
|
||||
AZ_Warning("ProjectManager::GemCatalog", false,
|
||||
"Cannot find entry for gem with name '%s'. The CMake target name probably does not match the specified name in the gem.json.",
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace O3DE::ProjectManager
|
||||
DownloadController* GetDownloadController() const { return m_downloadController; }
|
||||
|
||||
public slots:
|
||||
void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies);
|
||||
void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
|
||||
void OnAddGemClicked();
|
||||
|
||||
protected:
|
||||
|
||||
@@ -27,6 +27,14 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemModel::AddGem(const GemInfo& gemInfo)
|
||||
{
|
||||
if (FindIndexByNameString(gemInfo.m_name).isValid())
|
||||
{
|
||||
// do not add gems with duplicate names
|
||||
// this can happen by mistake or when a gem repo has a gem with the same name as a local gem
|
||||
AZ_TracePrintf("GemModel", "Ignoring duplicate gem: %s", gemInfo.m_name.toUtf8().constData());
|
||||
return;
|
||||
}
|
||||
|
||||
QStandardItem* item = new QStandardItem();
|
||||
|
||||
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
@@ -60,6 +68,7 @@ namespace O3DE::ProjectManager
|
||||
void GemModel::Clear()
|
||||
{
|
||||
clear();
|
||||
m_nameToIndexMap.clear();
|
||||
}
|
||||
|
||||
void GemModel::UpdateGemDependencies()
|
||||
@@ -276,9 +285,11 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemModel::SetIsAdded(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
|
||||
{
|
||||
// get the gemName first, because the modelIndex data change after adding because of filters
|
||||
QString gemName = modelIndex.data(RoleName).toString();
|
||||
model.setData(modelIndex, isAdded, RoleIsAdded);
|
||||
|
||||
UpdateDependencies(model, modelIndex);
|
||||
UpdateDependencies(model, gemName, isAdded);
|
||||
}
|
||||
|
||||
bool GemModel::HasDependentGems(const QModelIndex& modelIndex) const
|
||||
@@ -294,15 +305,17 @@ namespace O3DE::ProjectManager
|
||||
return false;
|
||||
}
|
||||
|
||||
void GemModel::UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex)
|
||||
void GemModel::UpdateDependencies(QAbstractItemModel& model, const QString& gemName, bool isAdded)
|
||||
{
|
||||
GemModel* gemModel = GetSourceModel(&model);
|
||||
AZ_Assert(gemModel, "Failed to obtain GemModel");
|
||||
|
||||
QModelIndex modelIndex = gemModel->FindIndexByNameString(gemName);
|
||||
|
||||
QVector<QModelIndex> dependencies = gemModel->GatherGemDependencies(modelIndex);
|
||||
uint32_t numChangedDependencies = 0;
|
||||
|
||||
if (IsAdded(modelIndex))
|
||||
if (isAdded)
|
||||
{
|
||||
for (const QModelIndex& dependency : dependencies)
|
||||
{
|
||||
@@ -324,7 +337,7 @@ namespace O3DE::ProjectManager
|
||||
bool hasDependentGems = gemModel->HasDependentGems(modelIndex);
|
||||
if (IsAddedDependency(modelIndex) != hasDependentGems)
|
||||
{
|
||||
SetIsAddedDependency(model, modelIndex, hasDependentGems);
|
||||
SetIsAddedDependency(*gemModel, modelIndex, hasDependentGems);
|
||||
}
|
||||
|
||||
for (const QModelIndex& dependency : dependencies)
|
||||
@@ -343,7 +356,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
gemModel->emit gemStatusChanged(modelIndex, numChangedDependencies);
|
||||
gemModel->emit gemStatusChanged(gemName, numChangedDependencies);
|
||||
}
|
||||
|
||||
void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace O3DE::ProjectManager
|
||||
static bool NeedsToBeAdded(const QModelIndex& modelIndex, bool includeDependencies = false);
|
||||
static bool NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies = false);
|
||||
static bool HasRequirement(const QModelIndex& modelIndex);
|
||||
static void UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex);
|
||||
static void UpdateDependencies(QAbstractItemModel& model, const QString& gemName, bool isAdded);
|
||||
static void SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status);
|
||||
|
||||
bool DoGemsToBeAddedHaveRequirements() const;
|
||||
@@ -78,7 +78,7 @@ namespace O3DE::ProjectManager
|
||||
int TotalAddedGems(bool includeDependencies = false) const;
|
||||
|
||||
signals:
|
||||
void gemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies);
|
||||
void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
|
||||
|
||||
private:
|
||||
void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames);
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QLabel* projectPreviewLabel = new QLabel(tr("Select an image (PNG). Minimum %1 x %2 pixels.")
|
||||
.arg(QString::number(ProjectPreviewImageWidth), QString::number(ProjectPreviewImageHeight)));
|
||||
projectPreviewLabel->setObjectName("projectPreviewLabel");
|
||||
previewExtrasLayout->addWidget(projectPreviewLabel);
|
||||
|
||||
m_projectPreviewImage = new QLabel(this);
|
||||
|
||||
@@ -78,5 +78,10 @@ namespace AZ
|
||||
//! Find an assignment id corresponding to the lod and label substring filters
|
||||
MaterialAssignmentId FindMaterialAssignmentIdInModel(
|
||||
const Data::Instance<AZ::RPI::Model>& model, const MaterialAssignmentLodIndex lodFilter, const AZStd::string& labelFilter);
|
||||
|
||||
//! Special case handling to convert script values to supported types
|
||||
AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueFromScript(
|
||||
const AZ::RPI::MaterialPropertyDescriptor* propertyDescriptor, const AZStd::any& value);
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/PassFilter.h>
|
||||
#include <Atom/RPI.Public/Pass/RenderPass.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
|
||||
#include <Atom/RPI.Public/ViewportContextManager.h>
|
||||
|
||||
|
||||
@@ -143,28 +143,34 @@ namespace AZ
|
||||
{
|
||||
bool wasRenamed = false;
|
||||
Name newName;
|
||||
RPI::MaterialPropertyIndex materialPropertyIndex = m_materialInstance->FindPropertyIndex(propertyPair.first, &wasRenamed, &newName);
|
||||
RPI::MaterialPropertyIndex materialPropertyIndex =
|
||||
m_materialInstance->FindPropertyIndex(propertyPair.first, &wasRenamed, &newName);
|
||||
|
||||
// FindPropertyIndex will have already reported a message about what the old and new names are. Here we just add some extra info to help the user resolve it.
|
||||
AZ_Warning("MaterialAssignment", !wasRenamed,
|
||||
// FindPropertyIndex will have already reported a message about what the old and new names are. Here we just add
|
||||
// some extra info to help the user resolve it.
|
||||
AZ_Warning(
|
||||
"MaterialAssignment", !wasRenamed,
|
||||
"Consider running \"Apply Automatic Property Updates\" to use the latest property names.",
|
||||
propertyPair.first.GetCStr(),
|
||||
newName.GetCStr());
|
||||
propertyPair.first.GetCStr(), newName.GetCStr());
|
||||
|
||||
if (wasRenamed && m_propertyOverrides.find(newName) != m_propertyOverrides.end())
|
||||
{
|
||||
materialPropertyIndex.Reset();
|
||||
|
||||
AZ_Warning("MaterialAssignment", false,
|
||||
"Material property '%s' has been renamed to '%s', and a property override exists for both. The one with the old name will be ignored.",
|
||||
propertyPair.first.GetCStr(),
|
||||
newName.GetCStr());
|
||||
|
||||
AZ_Warning(
|
||||
"MaterialAssignment", false,
|
||||
"Material property '%s' has been renamed to '%s', and a property override exists for both. The one with "
|
||||
"the old name will be ignored.",
|
||||
propertyPair.first.GetCStr(), newName.GetCStr());
|
||||
}
|
||||
|
||||
if (!materialPropertyIndex.IsNull())
|
||||
{
|
||||
const auto propertyDescriptor =
|
||||
m_materialInstance->GetMaterialPropertiesLayout()->GetPropertyDescriptor(materialPropertyIndex);
|
||||
|
||||
m_materialInstance->SetPropertyValue(
|
||||
materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second));
|
||||
materialPropertyIndex, ConvertMaterialPropertyValueFromScript(propertyDescriptor, propertyPair.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,5 +290,58 @@ namespace AZ
|
||||
|
||||
return MaterialAssignmentId();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueNumericType(const AZStd::any& value)
|
||||
{
|
||||
if (value.is<int32_t>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<int32_t>(value));
|
||||
}
|
||||
if (value.is<uint32_t>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<uint32_t>(value));
|
||||
}
|
||||
if (value.is<float>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<float>(value));
|
||||
}
|
||||
if (value.is<double>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<double>(value));
|
||||
}
|
||||
|
||||
return AZ::RPI::MaterialPropertyValue::FromAny(value);
|
||||
}
|
||||
|
||||
AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueFromScript(
|
||||
const AZ::RPI::MaterialPropertyDescriptor* propertyDescriptor, const AZStd::any& value)
|
||||
{
|
||||
switch (propertyDescriptor->GetDataType())
|
||||
{
|
||||
case AZ::RPI::MaterialPropertyDataType::Enum:
|
||||
if (value.is<AZ::Name>())
|
||||
{
|
||||
return propertyDescriptor->GetEnumValue(AZStd::any_cast<AZ::Name>(value));
|
||||
}
|
||||
if (value.is<AZStd::string>())
|
||||
{
|
||||
return propertyDescriptor->GetEnumValue(AZ::Name(AZStd::any_cast<AZStd::string>(value)));
|
||||
}
|
||||
return ConvertMaterialPropertyValueNumericType<uint32_t>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::Int:
|
||||
return ConvertMaterialPropertyValueNumericType<int32_t>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::UInt:
|
||||
return ConvertMaterialPropertyValueNumericType<uint32_t>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::Float:
|
||||
return ConvertMaterialPropertyValueNumericType<float>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::Bool:
|
||||
return ConvertMaterialPropertyValueNumericType<bool>(value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return AZ::RPI::MaterialPropertyValue::FromAny(value);
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -107,7 +107,7 @@ ly_add_target(
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Atom_RHI_DX12.Reflect
|
||||
3rdParty::d3dx12
|
||||
${AFTERMATH_BUILD_DEPENDENCY}
|
||||
${AFTERMATH_BUILD_DEPENDENCY}
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
${USE_NSIGHT_AFTERMATH_DEFINE}
|
||||
@@ -128,7 +128,6 @@ ly_add_target(
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Atom_RHI.Public
|
||||
Gem::Atom_RHI_DX12.Reflect
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <Atom/RPI.Public/GpuQuery/GpuQuerySystemInterface.h>
|
||||
#include <Atom/RPI.Reflect/Image/Image.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImage.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImage.h>
|
||||
#include <Atom/RPI.Public/Pass/PassAttachment.h>
|
||||
#include <Atom/RPI.Public/Pass/PassDefines.h>
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
@@ -59,6 +60,7 @@ namespace AZ
|
||||
struct PassRequest;
|
||||
struct PassValidationResults;
|
||||
class AttachmentReadback;
|
||||
class ImageAttachmentCopy;
|
||||
|
||||
using SortedPipelineViewTags = AZStd::set<PipelineViewTag, AZNameSortAscending>;
|
||||
using PassesByDrawList = AZStd::map<RHI::DrawListTag, const Pass*>;
|
||||
@@ -94,6 +96,8 @@ namespace AZ
|
||||
{
|
||||
AZ_RPI_PASS(Pass);
|
||||
|
||||
friend class ImageAttachmentPreviewPass;
|
||||
|
||||
public:
|
||||
using ChildPassIndex = RHI::Handle<uint32_t, class ChildPass>;
|
||||
|
||||
@@ -369,6 +373,9 @@ namespace AZ
|
||||
|
||||
void UpdateReadbackAttachment(FramePrepareParams params, bool beforeAddScopes);
|
||||
|
||||
// Setup ImageAttachmentCopy
|
||||
void UpdateAttachmentCopy(FramePrepareParams params);
|
||||
|
||||
// --- Protected Members ---
|
||||
|
||||
const Name PassNameThis{"This"};
|
||||
@@ -466,6 +473,9 @@ namespace AZ
|
||||
AZStd::shared_ptr<AttachmentReadback> m_attachmentReadback;
|
||||
PassAttachmentReadbackOption m_readbackOption;
|
||||
|
||||
// For image attachment preview
|
||||
AZStd::weak_ptr<ImageAttachmentCopy> m_attachmentCopy;
|
||||
|
||||
private:
|
||||
// Return the Timestamp result of this pass
|
||||
virtual TimestampResult GetTimestampResultInternal() const;
|
||||
|
||||
@@ -77,6 +77,16 @@ namespace AZ
|
||||
const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const;
|
||||
const AZStd::vector<Pass*>& GetPassesForTemplate(const Name& templateName) const;
|
||||
|
||||
//! Removes a PassTemplate by name, only if the following two conditions are met:
|
||||
//! 1- The template was NOT created from an Asset. This means the template will be erasable
|
||||
//! only if it was created at runtime with C++.
|
||||
//! 2- The are no instantiated Passes referencing such template.
|
||||
//! If the template exists but both conditions are not met then the function will assert.
|
||||
//! If a template with the given name doesn't exist the function does nothing.
|
||||
//! This function should be used judiciously, and under rare circumstances. For example,
|
||||
//! Applications that iteratively create and need to delete templates at runtime.
|
||||
void RemovePassTemplate(const Name& name);
|
||||
|
||||
//! Removes a pass from both it's associated template (if it has one) and from the pass name mapping
|
||||
void RemovePassFromLibrary(Pass* pass);
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace AZ
|
||||
bool HasPassesForTemplateName(const Name& templateName) const override;
|
||||
bool AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate) override;
|
||||
const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const override;
|
||||
void RemovePassTemplate(const Name& name) override;
|
||||
void RemovePassFromLibrary(Pass* pass) override;
|
||||
void RegisterPass(Pass* pass) override;
|
||||
void UnregisterPass(Pass* pass) override;
|
||||
|
||||
@@ -199,6 +199,9 @@ namespace AZ
|
||||
//! Retrieves a PassTemplate from the library
|
||||
virtual const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const = 0;
|
||||
|
||||
//! See remarks in PassLibrary.h for the function with this name.
|
||||
virtual void RemovePassTemplate(const Name& name) = 0;
|
||||
|
||||
//! Removes all references to the given pass from the pass library
|
||||
virtual void RemovePassFromLibrary(Pass* pass) = 0;
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
#include <Atom/RHI/DrawList.h>
|
||||
#include <Atom/RHI/ScopeProducer.h>
|
||||
|
||||
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
|
||||
#include <Atom/RPI.Public/Pass/Pass.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -29,7 +27,6 @@ namespace AZ
|
||||
|
||||
namespace RPI
|
||||
{
|
||||
class ImageAttachmentCopy;
|
||||
class RenderPass;
|
||||
class Query;
|
||||
|
||||
@@ -41,8 +38,6 @@ namespace AZ
|
||||
{
|
||||
AZ_RPI_PASS(RenderPass);
|
||||
|
||||
friend class ImageAttachmentPreviewPass;
|
||||
|
||||
using ScopeQuery = AZStd::array<RHI::Ptr<Query>, static_cast<size_t>(ScopeQueryType::Count)>;
|
||||
|
||||
public:
|
||||
@@ -143,8 +138,6 @@ namespace AZ
|
||||
// Readback the results from the ScopeQueries
|
||||
void ReadbackScopeQueryResults();
|
||||
|
||||
AZStd::weak_ptr<ImageAttachmentCopy> m_attachmentCopy;
|
||||
|
||||
// Readback results from the Timestamp queries
|
||||
TimestampResult m_timestampResult;
|
||||
// Readback results from the PipelineStatistics queries
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ namespace AZ
|
||||
~ImageAttachmentPreviewPass();
|
||||
|
||||
//! Preview the PassAttachment of a pass' PassAttachmentBinding
|
||||
void PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment);
|
||||
void PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment);
|
||||
|
||||
//! Set the output color attachment for this pass
|
||||
void SetOutputColorAttachment(RHI::Ptr<PassAttachment> outputImageAttachment);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <Atom/RPI.Public/Pass/PassLibrary.h>
|
||||
#include <Atom/RPI.Public/Pass/PassDefines.h>
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
|
||||
@@ -1215,6 +1216,12 @@ namespace AZ
|
||||
m_queueState = PassQueueState::NoQueue;
|
||||
|
||||
InitializeInternal();
|
||||
|
||||
// Need to recreate the dest attachment because the source attachment might be changed
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->InvalidateDestImage();
|
||||
}
|
||||
|
||||
m_state = PassState::Initialized;
|
||||
}
|
||||
@@ -1301,6 +1308,9 @@ namespace AZ
|
||||
// readback attachment with output state
|
||||
UpdateReadbackAttachment(params, false);
|
||||
|
||||
// update attachment copy for preview
|
||||
UpdateAttachmentCopy(params);
|
||||
|
||||
UpdateConnectedOutputBindings();
|
||||
}
|
||||
|
||||
@@ -1489,6 +1499,14 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void Pass::UpdateAttachmentCopy(FramePrepareParams params)
|
||||
{
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->FrameBegin(params);
|
||||
}
|
||||
}
|
||||
|
||||
bool Pass::IsTimestampQueryEnabled() const
|
||||
{
|
||||
return m_flags.m_timestampQueryEnabled;
|
||||
|
||||
@@ -236,6 +236,19 @@ namespace AZ
|
||||
return true;
|
||||
}
|
||||
|
||||
void PassLibrary::RemovePassTemplate(const Name& name)
|
||||
{
|
||||
auto itr = m_templateEntries.find(name);
|
||||
if (itr != m_templateEntries.end())
|
||||
{
|
||||
AZ_Assert(itr->second.m_passes.empty(), "Can not delete PassTemplate '%s' because there are %zu Passes referencing it",
|
||||
name.GetCStr(), itr->second.m_passes.size());
|
||||
AZ_Assert(!itr->second.m_mappingAssetId.IsValid(), "Can not delete PassTemplate '%s' because it was created from an asset",
|
||||
name.GetCStr());
|
||||
m_templateEntries.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
void PassLibrary::RemovePassFromLibrary(Pass* pass)
|
||||
{
|
||||
if (m_isShuttingDown)
|
||||
|
||||
@@ -466,6 +466,11 @@ namespace AZ
|
||||
return m_passLibrary.GetPassTemplate(name);
|
||||
}
|
||||
|
||||
void PassSystem::RemovePassTemplate(const Name& name)
|
||||
{
|
||||
m_passLibrary.RemovePassTemplate(name);
|
||||
}
|
||||
|
||||
void PassSystem::RemovePassFromLibrary(Pass* pass)
|
||||
{
|
||||
m_passLibrary.RemovePassFromLibrary(pass);
|
||||
|
||||
@@ -177,12 +177,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to recreate the dest attachment because the source attachment might be changed
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->InvalidateDestImage();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::FrameBeginInternal(FramePrepareParams params)
|
||||
@@ -196,11 +190,7 @@ namespace AZ
|
||||
|
||||
// Read back the ScopeQueries submitted from previous frames
|
||||
ReadbackScopeQueryResults();
|
||||
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->FrameBegin(params);
|
||||
}
|
||||
|
||||
CollectSrgs();
|
||||
|
||||
PassSystemInterface::Get()->IncrementFrameRenderPassCount();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <Atom/RPI.Public/Buffer/Buffer.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImagePool.h>
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/RenderPass.h>
|
||||
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/Pass/ParentPass.h>
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
@@ -131,7 +131,7 @@ namespace AZ
|
||||
Data::AssetBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment)
|
||||
void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment)
|
||||
{
|
||||
if (passAttachment->GetAttachmentType() != RHI::AttachmentType::Image)
|
||||
{
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace AZ
|
||||
bool m_showAttachments = false;
|
||||
|
||||
AZ::RPI::Pass* m_selectedPass = nullptr;
|
||||
AZ::RPI::Pass* m_lastSelectedPass = nullptr;
|
||||
AZ::Name m_selectedPassPath;
|
||||
AZ::RHI::AttachmentId m_attachmentId;
|
||||
AZ::Name m_slotName;
|
||||
bool m_selectedChanged = false;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
namespace AZ::Render
|
||||
{
|
||||
inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::RenderPass* pass, AZ::RHI::AttachmentId attachmentId)
|
||||
inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::Pass* pass, AZ::RHI::AttachmentId attachmentId)
|
||||
{
|
||||
for (auto& binding : pass->GetAttachmentBindings())
|
||||
{
|
||||
@@ -47,6 +47,10 @@ namespace AZ::Render
|
||||
{
|
||||
using namespace AZ;
|
||||
|
||||
// always set m_selectedPass to empty and use m_selectedPassPath to find it when render the pass tree
|
||||
m_selectedPass = nullptr;
|
||||
bool needSaveAttachment = false;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(200.f, 200.f), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin("PassTree View", &draw, ImGuiWindowFlags_None))
|
||||
{
|
||||
@@ -83,60 +87,16 @@ namespace AZ::Render
|
||||
|
||||
if (Scriptable_ImGui::Button("Save Attachment"))
|
||||
{
|
||||
m_attachmentReadbackInfo = "";
|
||||
if (!m_readback)
|
||||
{
|
||||
m_readback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "AttachmentReadback" });
|
||||
m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1));
|
||||
}
|
||||
|
||||
if (m_selectedPass && !m_slotName.IsEmpty())
|
||||
{
|
||||
bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName);
|
||||
if (!readbackResult)
|
||||
{
|
||||
AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr());
|
||||
}
|
||||
}
|
||||
needSaveAttachment = true;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("%s", m_attachmentReadbackInfo.c_str());
|
||||
}
|
||||
|
||||
if (m_previewAttachment && m_selectedChanged)
|
||||
{
|
||||
m_selectedChanged = false;
|
||||
if (!m_attachmentId.IsEmpty() && m_selectedPass)
|
||||
{
|
||||
AZ::RPI::RenderPass* renderPass = azrtti_cast<AZ::RPI::RenderPass*>(m_selectedPass);
|
||||
if (renderPass)
|
||||
{
|
||||
if (!m_previewPass->GetParent())
|
||||
{
|
||||
RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass);
|
||||
}
|
||||
AZ::RPI::PassAttachment* attachment = FindPassAttachment(renderPass, m_attachmentId);
|
||||
if (attachment)
|
||||
{
|
||||
// Reset output attachment to empty so the preview will use pass's owner render pipeline's output
|
||||
m_previewPass->SetOutputColorAttachment(nullptr);
|
||||
m_previewPass->PreviewImageAttachmentForPass(renderPass, attachment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_previewPass->ClearPreviewAttachment();
|
||||
if (m_previewPass->GetParent())
|
||||
{
|
||||
m_previewPass->QueueForRemoval();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
// Draw the hierarchical view
|
||||
// It will assign m_seletedPass if there is a pass matches m_seletedPassPath
|
||||
ImGui::SetNextWindowPos(ImVec2(300, 60), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(300, 500), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin("PassTree", nullptr, ImGuiWindowFlags_None))
|
||||
@@ -144,6 +104,63 @@ namespace AZ::Render
|
||||
DrawTreeView(rootPass);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
// It's possible that the pass pointer changed but selected pass path wasn't changed
|
||||
if (m_selectedPass != m_lastSelectedPass)
|
||||
{
|
||||
m_selectedChanged = true;
|
||||
if (m_selectedPass == nullptr)
|
||||
{
|
||||
m_selectedPassPath = AZ::Name{};
|
||||
}
|
||||
}
|
||||
m_lastSelectedPass = m_selectedPass;
|
||||
|
||||
if (m_previewAttachment && m_selectedChanged)
|
||||
{
|
||||
m_selectedChanged = false;
|
||||
if (!m_attachmentId.IsEmpty() && m_selectedPass)
|
||||
{
|
||||
if (!m_previewPass->GetParent())
|
||||
{
|
||||
RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass);
|
||||
}
|
||||
AZ::RPI::PassAttachment* attachment = FindPassAttachment(m_selectedPass, m_attachmentId);
|
||||
if (attachment)
|
||||
{
|
||||
// Reset output attachment to empty so the preview will use pass's owner render pipeline's output
|
||||
m_previewPass->SetOutputColorAttachment(nullptr);
|
||||
m_previewPass->PreviewImageAttachmentForPass(m_selectedPass, attachment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_previewPass->ClearPreviewAttachment();
|
||||
if (m_previewPass->GetParent())
|
||||
{
|
||||
m_previewPass->QueueForRemoval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needSaveAttachment)
|
||||
{
|
||||
m_attachmentReadbackInfo = "";
|
||||
if (!m_readback)
|
||||
{
|
||||
m_readback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "AttachmentReadback" });
|
||||
m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1));
|
||||
}
|
||||
|
||||
if (m_selectedPass && !m_slotName.IsEmpty())
|
||||
{
|
||||
bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName);
|
||||
if (!readbackResult)
|
||||
{
|
||||
AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void ImGuiPassTree::DrawPassAttachments(AZ::RPI::Pass* pass)
|
||||
@@ -202,6 +219,7 @@ namespace AZ::Render
|
||||
|
||||
if (Scriptable_ImGui::Selectable(label.c_str(), m_attachmentId == binding.m_attachment->GetAttachmentId()))
|
||||
{
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_selectedPass = pass;
|
||||
m_attachmentId = binding.m_attachment->GetAttachmentId();
|
||||
m_slotName = binding.m_name;
|
||||
@@ -232,9 +250,9 @@ namespace AZ::Render
|
||||
if (!m_showAttachments)
|
||||
{
|
||||
// Only draw the leaf pass as selectable if we are not showing attachments as its children
|
||||
if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPass == pass))
|
||||
if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPassPath == pass->GetPathName()))
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = true;
|
||||
@@ -244,13 +262,13 @@ namespace AZ::Render
|
||||
{
|
||||
// Draw the pass as a tree node which has attachments as its children
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen
|
||||
| ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
| ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
|
||||
bool nodeOpen = Scriptable_ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags);
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = true;
|
||||
@@ -259,7 +277,6 @@ namespace AZ::Render
|
||||
if (nodeOpen)
|
||||
{
|
||||
DrawPassAttachments(pass);
|
||||
|
||||
Scriptable_ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
@@ -268,13 +285,13 @@ namespace AZ::Render
|
||||
{
|
||||
// For a ParentPasse, draw it as a tree node
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen
|
||||
| ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
| ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
|
||||
bool nodeOpen = ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags);
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = true;
|
||||
@@ -282,7 +299,10 @@ namespace AZ::Render
|
||||
|
||||
if (nodeOpen)
|
||||
{
|
||||
DrawPassAttachments(pass);
|
||||
if (m_showAttachments)
|
||||
{
|
||||
DrawPassAttachments(pass);
|
||||
}
|
||||
for (const auto& child : asParent->GetChildren())
|
||||
{
|
||||
DrawTreeView(child.get());
|
||||
@@ -296,6 +316,12 @@ namespace AZ::Render
|
||||
{
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// set m_selectedPass if pass path matches
|
||||
if (pass->GetPathName() == m_selectedPassPath)
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ImGuiPassTree::ReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
|
||||
@@ -364,7 +390,9 @@ namespace AZ::Render
|
||||
m_previewAttachment = false;
|
||||
m_showAttachments = false;
|
||||
|
||||
m_selectedPassPath = AZ::Name{};
|
||||
m_selectedPass = nullptr;
|
||||
m_lastSelectedPass = nullptr;
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = false;
|
||||
|
||||
+2
-1
@@ -130,7 +130,8 @@ namespace AZ::Render
|
||||
}
|
||||
AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext();
|
||||
|
||||
if (!m_fontDrawInterface || !viewportContext || !viewportContext->GetRenderScene())
|
||||
if (!m_fontDrawInterface || !viewportContext || !viewportContext->GetRenderScene() ||
|
||||
!AZ::Interface<AzFramework::FontQueryInterface>::Get())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
+15
-44
@@ -60,52 +60,8 @@ namespace AZ
|
||||
virtual void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) = 0;
|
||||
//! Set a material property override value wrapped by an AZStd::any
|
||||
virtual void SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) = 0;
|
||||
//! Set a material property override value to a bool
|
||||
virtual void SetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value) = 0;
|
||||
//! Set a material property override value to a integer
|
||||
virtual void SetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value) = 0;
|
||||
//! Set a material property override value to a unsigned integer
|
||||
virtual void SetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value) = 0;
|
||||
//! Set a material property override value to a float
|
||||
virtual void SetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value) = 0;
|
||||
//! Set a material property override value to a Vector2
|
||||
virtual void SetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value) = 0;
|
||||
//! Set a material property override value to a Vector3
|
||||
virtual void SetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value) = 0;
|
||||
//! Set a material property override value to a Vector4
|
||||
virtual void SetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value) = 0;
|
||||
//! Set a material property override value to a color
|
||||
virtual void SetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value) = 0;
|
||||
//! Set a material property override value to an image asset
|
||||
virtual void SetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Asset<AZ::RPI::ImageAsset>& value) = 0;
|
||||
//! Set a material property override value to an image instance
|
||||
virtual void SetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Instance<AZ::RPI::Image>& value) = 0;
|
||||
//! Set a material property override value to a string
|
||||
virtual void SetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value) = 0;
|
||||
//! Get a material property override value wrapped by an AZStd::any
|
||||
virtual AZStd::any GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a bool
|
||||
virtual bool GetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an integer
|
||||
virtual int32_t GetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an unsigned integer
|
||||
virtual uint32_t GetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a float
|
||||
virtual float GetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Vector2
|
||||
virtual AZ::Vector2 GetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Vector3
|
||||
virtual AZ::Vector3 GetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Vector4
|
||||
virtual AZ::Vector4 GetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Color
|
||||
virtual AZ::Color GetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an image asset
|
||||
virtual AZ::Data::Asset<AZ::RPI::ImageAsset> GetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an image instance
|
||||
virtual AZ::Data::Instance<AZ::RPI::Image> GetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a string
|
||||
virtual AZStd::string GetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Clear property override for a specific material assignment
|
||||
virtual void ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) = 0;
|
||||
//! Clear property overrides for a specific material assignment
|
||||
@@ -122,6 +78,21 @@ namespace AZ
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) = 0;
|
||||
//! Get Model UV overrides for a specific material assignment
|
||||
virtual AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const = 0;
|
||||
|
||||
//! Set material property override value with a specific type
|
||||
template<typename T>
|
||||
void SetPropertyOverrideT(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const T& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
//! Get material property override value with a specific type
|
||||
template<typename T>
|
||||
T GetPropertyOverrideT(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<T>() ? AZStd::any_cast<T>(value) : T{};
|
||||
}
|
||||
};
|
||||
using MaterialComponentRequestBus = EBus<MaterialComponentRequests>;
|
||||
|
||||
|
||||
+13
-4
@@ -314,12 +314,21 @@ namespace AZ
|
||||
|
||||
AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, propertyDefinition);
|
||||
|
||||
const auto& propertyIndex =
|
||||
m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id);
|
||||
|
||||
propertyConfig.m_groupName = groupDisplayName;
|
||||
const auto& propertyIndex = m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id);
|
||||
propertyConfig.m_showThumbnail = true;
|
||||
propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]);
|
||||
propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]);
|
||||
propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]);
|
||||
|
||||
propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType(
|
||||
m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]);
|
||||
|
||||
// There is no explicit parent material here. Material instance property overrides replace the values from the
|
||||
// assigned material asset. Its values should be treated as parent, for comparison, in this case.
|
||||
propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType(
|
||||
m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]);
|
||||
propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType(
|
||||
m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]);
|
||||
group.m_properties.emplace_back(propertyConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -196,6 +196,7 @@ namespace AZ
|
||||
{
|
||||
AZ_UNUSED(entityId);
|
||||
AZ_UNUSED(materialAssignmentId);
|
||||
|
||||
AZ_Warning(
|
||||
"EditorMaterialSystemComponent", false, "RenderMaterialPreview capture failed for entity %s slot %s.",
|
||||
entityId.ToString().c_str(), materialAssignmentId.ToString().c_str());
|
||||
|
||||
+32
-171
@@ -54,29 +54,29 @@ namespace AZ
|
||||
->Event("GetMaterialOverride", &MaterialComponentRequestBus::Events::GetMaterialOverride)
|
||||
->Event("ClearMaterialOverride", &MaterialComponentRequestBus::Events::ClearMaterialOverride)
|
||||
->Event("SetPropertyOverride", &MaterialComponentRequestBus::Events::SetPropertyOverride)
|
||||
->Event("SetPropertyOverrideBool", &MaterialComponentRequestBus::Events::SetPropertyOverrideBool)
|
||||
->Event("SetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideInt32)
|
||||
->Event("SetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideUInt32)
|
||||
->Event("SetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::SetPropertyOverrideFloat)
|
||||
->Event("SetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector2)
|
||||
->Event("SetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector3)
|
||||
->Event("SetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector4)
|
||||
->Event("SetPropertyOverrideColor", &MaterialComponentRequestBus::Events::SetPropertyOverrideColor)
|
||||
->Event("SetPropertyOverrideImageAsset", &MaterialComponentRequestBus::Events::SetPropertyOverrideImageAsset)
|
||||
->Event("SetPropertyOverrideImageInstance", &MaterialComponentRequestBus::Events::SetPropertyOverrideImageInstance)
|
||||
->Event("SetPropertyOverrideString", &MaterialComponentRequestBus::Events::SetPropertyOverrideString)
|
||||
->Event("SetPropertyOverrideBool", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<bool>)
|
||||
->Event("SetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<int32_t>)
|
||||
->Event("SetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<uint32_t>)
|
||||
->Event("SetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<float>)
|
||||
->Event("SetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector2>)
|
||||
->Event("SetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector3>)
|
||||
->Event("SetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector4>)
|
||||
->Event("SetPropertyOverrideColor", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Color>)
|
||||
->Event("SetPropertyOverrideImage", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Data::AssetId>)
|
||||
->Event("SetPropertyOverrideString", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZStd::string>)
|
||||
->Event("SetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<uint32_t>)
|
||||
->Event("GetPropertyOverride", &MaterialComponentRequestBus::Events::GetPropertyOverride)
|
||||
->Event("GetPropertyOverrideBool", &MaterialComponentRequestBus::Events::GetPropertyOverrideBool)
|
||||
->Event("GetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideInt32)
|
||||
->Event("GetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideUInt32)
|
||||
->Event("GetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::GetPropertyOverrideFloat)
|
||||
->Event("GetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector2)
|
||||
->Event("GetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector3)
|
||||
->Event("GetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector4)
|
||||
->Event("GetPropertyOverrideColor", &MaterialComponentRequestBus::Events::GetPropertyOverrideColor)
|
||||
->Event("GetPropertyOverrideImageAsset", &MaterialComponentRequestBus::Events::GetPropertyOverrideImageAsset)
|
||||
->Event("GetPropertyOverrideImageInstance", &MaterialComponentRequestBus::Events::GetPropertyOverrideImageInstance)
|
||||
->Event("GetPropertyOverrideString", &MaterialComponentRequestBus::Events::GetPropertyOverrideString)
|
||||
->Event("GetPropertyOverrideBool", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<bool>)
|
||||
->Event("GetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<int32_t>)
|
||||
->Event("GetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<uint32_t>)
|
||||
->Event("GetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<float>)
|
||||
->Event("GetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector2>)
|
||||
->Event("GetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector3>)
|
||||
->Event("GetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector4>)
|
||||
->Event("GetPropertyOverrideColor", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Color>)
|
||||
->Event("GetPropertyOverrideImage", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Data::AssetId>)
|
||||
->Event("GetPropertyOverrideString", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZStd::string>)
|
||||
->Event("GetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<uint32_t>)
|
||||
->Event("ClearPropertyOverride", &MaterialComponentRequestBus::Events::ClearPropertyOverride)
|
||||
->Event("ClearPropertyOverrides", &MaterialComponentRequestBus::Events::ClearPropertyOverrides)
|
||||
->Event("ClearAllPropertyOverrides", &MaterialComponentRequestBus::Events::ClearAllPropertyOverrides)
|
||||
@@ -121,8 +121,13 @@ namespace AZ
|
||||
MaterialComponentRequestBus::Handler::BusDisconnect();
|
||||
MaterialReceiverNotificationBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
|
||||
ReleaseMaterials();
|
||||
|
||||
// Sending notification to wipe any previously assigned material overrides
|
||||
MaterialComponentNotificationBus::Event(
|
||||
m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, MaterialAssignmentMap());
|
||||
|
||||
m_queuedMaterialUpdateNotification = false;
|
||||
m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId);
|
||||
}
|
||||
@@ -221,6 +226,11 @@ namespace AZ
|
||||
if (!anyQueued)
|
||||
{
|
||||
ReleaseMaterials();
|
||||
|
||||
// If no other materials were loaded, the notification must still be sent in case there are externally managed material
|
||||
// instances in the configuration
|
||||
MaterialComponentNotificationBus::Event(
|
||||
m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,8 +278,6 @@ namespace AZ
|
||||
{
|
||||
materialPair.second.Release();
|
||||
}
|
||||
|
||||
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
|
||||
}
|
||||
|
||||
MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const
|
||||
@@ -499,76 +507,6 @@ namespace AZ
|
||||
QueuePropertyChanges(materialAssignmentId);
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideBool(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideUInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideFloat(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideVector2(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideVector3(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideVector4(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideColor(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideImageAsset(
|
||||
const MaterialAssignmentId& materialAssignmentId,
|
||||
const AZStd::string& propertyName,
|
||||
const AZ::Data::Asset<AZ::RPI::ImageAsset>& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideImageInstance(
|
||||
const MaterialAssignmentId& materialAssignmentId,
|
||||
const AZStd::string& propertyName,
|
||||
const AZ::Data::Instance<AZ::RPI::Image>& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideString(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
AZStd::any MaterialComponentController::GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
|
||||
@@ -586,83 +524,6 @@ namespace AZ
|
||||
return propertyIt->second;
|
||||
}
|
||||
|
||||
bool MaterialComponentController::GetPropertyOverrideBool(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<bool>() ? AZStd::any_cast<bool>(value) : false;
|
||||
}
|
||||
|
||||
int32_t MaterialComponentController::GetPropertyOverrideInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<int32_t>() ? AZStd::any_cast<int32_t>(value) : 0;
|
||||
}
|
||||
|
||||
uint32_t MaterialComponentController::GetPropertyOverrideUInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<uint32_t>() ? AZStd::any_cast<uint32_t>(value) : 0;
|
||||
}
|
||||
|
||||
float MaterialComponentController::GetPropertyOverrideFloat(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<float>() ? AZStd::any_cast<float>(value) : 0.0f;
|
||||
}
|
||||
|
||||
AZ::Vector2 MaterialComponentController::GetPropertyOverrideVector2(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Vector2>() ? AZStd::any_cast<AZ::Vector2>(value) : AZ::Vector2::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Vector3 MaterialComponentController::GetPropertyOverrideVector3(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Vector3>() ? AZStd::any_cast<AZ::Vector3>(value) : AZ::Vector3::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Vector4 MaterialComponentController::GetPropertyOverrideVector4(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Vector4>() ? AZStd::any_cast<AZ::Vector4>(value) : AZ::Vector4::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Color MaterialComponentController::GetPropertyOverrideColor(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Color>() ? AZStd::any_cast<AZ::Color>(value) : AZ::Color::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Data::Asset<AZ::RPI::ImageAsset> MaterialComponentController::GetPropertyOverrideImageAsset(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Data::Asset<AZ::RPI::ImageAsset>>() ? AZStd::any_cast<AZ::Data::Asset<AZ::RPI::ImageAsset>>(value) : AZ::Data::Asset<AZ::RPI::ImageAsset>();
|
||||
}
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::Image> MaterialComponentController::GetPropertyOverrideImageInstance(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Data::Instance<AZ::RPI::Image>>() ? AZStd::any_cast<AZ::Data::Instance<AZ::RPI::Image>>(value) : AZ::Data::Instance<AZ::RPI::Image>();
|
||||
}
|
||||
|
||||
AZStd::string MaterialComponentController::GetPropertyOverrideString(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZStd::string>() ? AZStd::any_cast<AZStd::string>(value) : AZStd::string();
|
||||
}
|
||||
|
||||
void MaterialComponentController::ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName)
|
||||
{
|
||||
auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
|
||||
|
||||
-25
@@ -65,33 +65,8 @@ namespace AZ
|
||||
void SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) override;
|
||||
AZ::Data::AssetId GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const override;
|
||||
void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) override;
|
||||
|
||||
void SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) override;
|
||||
void SetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value) override;
|
||||
void SetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value) override;
|
||||
void SetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value) override;
|
||||
void SetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value) override;
|
||||
void SetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value) override;
|
||||
void SetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value) override;
|
||||
void SetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value) override;
|
||||
void SetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value) override;
|
||||
void SetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Asset<AZ::RPI::ImageAsset>& value) override;
|
||||
void SetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Instance<AZ::RPI::Image>& value) override;
|
||||
void SetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value) override;
|
||||
|
||||
AZStd::any GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
bool GetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
int32_t GetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
uint32_t GetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
float GetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Vector2 GetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Vector3 GetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Vector4 GetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Color GetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Data::Asset<AZ::RPI::ImageAsset> GetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Data::Instance<AZ::RPI::Image> GetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZStd::string GetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
|
||||
void ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) override;
|
||||
void ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) override;
|
||||
void ClearAllPropertyOverrides() override;
|
||||
|
||||
@@ -105,6 +105,8 @@ namespace EMotionFX
|
||||
*jointTypeId, parentBindRotationWorld, nodeBindRotationWorld, boneDirection, exampleRotationsLocal);
|
||||
|
||||
AZ_Assert(jointLimitConfig, "Could not create joint limit configuration.");
|
||||
jointLimitConfig->SetPropertyVisibility(AzPhysics::JointConfiguration::PropertyVisibility::ParentLocalRotation, true);
|
||||
jointLimitConfig->SetPropertyVisibility(AzPhysics::JointConfiguration::PropertyVisibility::ChildLocalRotation, true);
|
||||
return jointLimitConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,7 +576,7 @@ namespace EMotionFX
|
||||
|
||||
// The configuration stores some debug option. When that is enabled, we override it on top of the render flags.
|
||||
m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds;
|
||||
m_debugRenderFlags[RENDER_SKELETON] = m_debugRenderFlags[RENDER_SKELETON] || m_configuration.m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_LINESKELETON] = m_debugRenderFlags[RENDER_LINESKELETON] || m_configuration.m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
|
||||
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ namespace EMotionFX
|
||||
m_renderActorInstance->UpdateBounds();
|
||||
|
||||
m_debugRenderFlags[RENDER_AABB] = m_renderBounds;
|
||||
m_debugRenderFlags[RENDER_SKELETON] = m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_LINESKELETON] = m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
|
||||
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ set(FILES
|
||||
Include/InAppPurchases/InAppPurchasesBus.h
|
||||
Include/InAppPurchases/InAppPurchasesInterface.h
|
||||
Include/InAppPurchases/InAppPurchasesResponseBus.h
|
||||
Source/InAppPurchasesSystemComponent.h
|
||||
Source/InAppPurchasesSystemComponent.cpp
|
||||
Source/InAppPurchasesInterface.cpp
|
||||
)
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace Multiplayer
|
||||
void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace Multiplayer
|
||||
void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
|
||||
@@ -17,6 +17,7 @@ if(PAL_TRAIT_PHYSX_SUPPORTED)
|
||||
set(physx_dependency 3rdParty::PhysX)
|
||||
set(physx_files physx_files.cmake)
|
||||
set(physx_shared_files physx_shared_files.cmake)
|
||||
set(physx_mock_files physx_mocks_files.cmake)
|
||||
set(physx_editor_files physx_editor_files.cmake)
|
||||
else()
|
||||
set(physx_files physx_unsupported_files.cmake)
|
||||
@@ -151,6 +152,17 @@ endif()
|
||||
# Tests
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME PhysX.Mocks HEADERONLY
|
||||
NAMESPACE Gem
|
||||
OUTPUT_NAME PhysX.Mocks.Gem
|
||||
FILES_CMAKE
|
||||
physx_mocks_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
INTERFACE
|
||||
Mocks
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME PhysX.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
@@ -213,6 +225,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
AZ::AzTest
|
||||
AZ::AzToolsFrameworkTestCommon
|
||||
Gem::PhysX.Static
|
||||
Gem::PhysX.Mocks
|
||||
Gem::PhysX.Editor.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::LmbrCentral.Editor
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 <gmock/gmock.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/Physics/HeightfieldProviderBus.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class MockPhysXHeightfieldProviderComponent
|
||||
: public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(MockPhysXHeightfieldProviderComponent, "{C5F7CCCF-FDB2-40DF-992D-CF028F4A1B59}");
|
||||
|
||||
static void Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<MockPhysXHeightfieldProviderComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Activate() override
|
||||
{
|
||||
}
|
||||
|
||||
void Deactivate() override
|
||||
{
|
||||
}
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService"));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class MockPhysXHeightfieldProvider
|
||||
: protected Physics::HeightfieldProviderRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
MockPhysXHeightfieldProvider(AZ::EntityId entityId)
|
||||
{
|
||||
Physics::HeightfieldProviderRequestsBus::Handler::BusConnect(entityId);
|
||||
}
|
||||
|
||||
~MockPhysXHeightfieldProvider()
|
||||
{
|
||||
Physics::HeightfieldProviderRequestsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_CONST_METHOD0(GetHeightsAndMaterials, AZStd::vector<Physics::HeightMaterialPoint>());
|
||||
MOCK_CONST_METHOD0(GetHeightfieldGridSpacing, AZ::Vector2());
|
||||
MOCK_CONST_METHOD2(GetHeightfieldGridSize, void(int32_t&, int32_t&));
|
||||
MOCK_CONST_METHOD2(GetHeightfieldHeightBounds, void(float&, float&));
|
||||
MOCK_CONST_METHOD0(GetHeightfieldTransform, AZ::Transform());
|
||||
MOCK_CONST_METHOD0(GetMaterialList, AZStd::vector<Physics::MaterialId>());
|
||||
MOCK_CONST_METHOD0(GetHeights, AZStd::vector<float>());
|
||||
MOCK_CONST_METHOD1(UpdateHeights, AZStd::vector<float>(const AZ::Aabb& dirtyRegion));
|
||||
MOCK_CONST_METHOD1(UpdateHeightsAndMaterials, AZStd::vector<Physics::HeightMaterialPoint>(const AZ::Aabb& dirtyRegion));
|
||||
MOCK_CONST_METHOD0(GetHeightfieldAabb, AZ::Aabb());
|
||||
};
|
||||
|
||||
} // namespace UnitTest
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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/UnitTest/TestTypes.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
|
||||
#include <Tests/EditorTestUtilities.h>
|
||||
#include <EditorHeightfieldColliderComponent.h>
|
||||
#include <HeightfieldColliderComponent.h>
|
||||
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
|
||||
#include <AzFramework/Physics/HeightfieldProviderBus.h>
|
||||
#include <StaticRigidBodyComponent.h>
|
||||
#include <RigidBodyStatic.h>
|
||||
#include <PhysX/PhysXLocks.h>
|
||||
#include <AzFramework/Physics/Components/SimulatedBodyComponentBus.h>
|
||||
#include <PhysX/MockPhysXHeightfieldProviderComponent.h>
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace PhysXEditorTests
|
||||
{
|
||||
AZStd::vector<Physics::HeightMaterialPoint> GetSamples()
|
||||
{
|
||||
AZStd::vector<Physics::HeightMaterialPoint> samples{ { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 2.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 1.5f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 0.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight } };
|
||||
return samples;
|
||||
}
|
||||
|
||||
EntityPtr SetupHeightfieldComponent()
|
||||
{
|
||||
// create an editor entity with a shape collider component and a box shape component
|
||||
EntityPtr editorEntity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
editorEntity->CreateComponent<UnitTest::MockPhysXHeightfieldProviderComponent>();
|
||||
editorEntity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId);
|
||||
editorEntity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
AZ::ComponentApplicationBus::Broadcast(
|
||||
&AZ::ComponentApplicationRequests::RegisterComponentDescriptor,
|
||||
UnitTest::MockPhysXHeightfieldProviderComponent::CreateDescriptor());
|
||||
return editorEntity;
|
||||
}
|
||||
|
||||
void CleanupHeightfieldComponent()
|
||||
{
|
||||
AZ::ComponentApplicationBus::Broadcast(
|
||||
&AZ::ComponentApplicationRequests::UnregisterComponentDescriptor,
|
||||
UnitTest::MockPhysXHeightfieldProviderComponent::CreateDescriptor());
|
||||
}
|
||||
|
||||
void SetupMockMethods(NiceMock<UnitTest::MockPhysXHeightfieldProvider>& mockShapeRequests)
|
||||
{
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldTransform).WillByDefault(Return(AZ::Transform::CreateTranslation({ 1, 2, 0 })));
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldGridSpacing).WillByDefault(Return(AZ::Vector2(1, 1)));
|
||||
ON_CALL(mockShapeRequests, GetHeightsAndMaterials).WillByDefault(Return(GetSamples()));
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldGridSize)
|
||||
.WillByDefault(
|
||||
[](int32_t& numColumns, int32_t& numRows)
|
||||
{
|
||||
numColumns = 3;
|
||||
numRows = 3;
|
||||
});
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldHeightBounds)
|
||||
.WillByDefault(
|
||||
[](float& x, float& y)
|
||||
{
|
||||
x = -3.0f;
|
||||
y = 3.0f;
|
||||
});
|
||||
}
|
||||
|
||||
EntityPtr TestCreateActiveGameEntityFromEditorEntity(AZ::Entity* editorEntity)
|
||||
{
|
||||
EntityPtr gameEntity = AZStd::make_unique<AZ::Entity>();
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequests::PreExportEntity, *editorEntity, *gameEntity);
|
||||
gameEntity->Init();
|
||||
return gameEntity;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesSatisfiedEntityIsValid)
|
||||
{
|
||||
EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
entity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId);
|
||||
entity->CreateComponent<UnitTest::MockPhysXHeightfieldProviderComponent>()->CreateDescriptor();
|
||||
|
||||
// the entity should be in a valid state because the shape component and
|
||||
// the Terrain Physics Collider Component requirement is satisfied.
|
||||
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
||||
EXPECT_TRUE(sortOutcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesMissingEntityIsInvalid)
|
||||
{
|
||||
EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
|
||||
// the entity should not be in a valid state because the heightfield collider component requires
|
||||
// a shape component and the Terrain Physics Collider Component
|
||||
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
||||
EXPECT_FALSE(sortOutcome.IsSuccess());
|
||||
EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::MissingRequiredService);
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentMultipleHeightfieldColliderComponentsEntityIsInvalid)
|
||||
{
|
||||
EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
entity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId);
|
||||
|
||||
// adding a second heightfield collider component should make the entity invalid
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
|
||||
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
||||
EXPECT_FALSE(sortOutcome.IsSuccess());
|
||||
EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::HasIncompatibleServices);
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithCorrectComponentsCorrectRuntimeComponents)
|
||||
{
|
||||
EntityPtr editorEntity = SetupHeightfieldComponent();
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests(editorEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests);
|
||||
editorEntity->Activate();
|
||||
|
||||
EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests2(gameEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests2);
|
||||
gameEntity->Activate();
|
||||
|
||||
// check that the runtime entity has the expected components
|
||||
EXPECT_TRUE(gameEntity->FindComponent<UnitTest::MockPhysXHeightfieldProviderComponent>() != nullptr);
|
||||
EXPECT_TRUE(gameEntity->FindComponent<PhysX::HeightfieldColliderComponent>() != nullptr);
|
||||
EXPECT_TRUE(gameEntity->FindComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId) != nullptr);
|
||||
|
||||
CleanupHeightfieldComponent();
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithAABoxCorrectRuntimeGeometry)
|
||||
{
|
||||
EntityPtr editorEntity = SetupHeightfieldComponent();
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests(editorEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests);
|
||||
editorEntity->Activate();
|
||||
|
||||
EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests2(gameEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests2);
|
||||
gameEntity->Activate();
|
||||
|
||||
AzPhysics::SimulatedBody* staticBody = nullptr;
|
||||
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(
|
||||
staticBody, gameEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody);
|
||||
const auto* pxRigidStatic = static_cast<const physx::PxRigidStatic*>(staticBody->GetNativePointer());
|
||||
|
||||
PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene());
|
||||
|
||||
// there should be a single shape on the rigid body and it should be a heightfield
|
||||
EXPECT_EQ(pxRigidStatic->getNbShapes(), 1);
|
||||
|
||||
physx::PxShape* shape = nullptr;
|
||||
pxRigidStatic->getShapes(&shape, 1, 0);
|
||||
EXPECT_EQ(shape->getGeometryType(), physx::PxGeometryType::eHEIGHTFIELD);
|
||||
|
||||
physx::PxHeightFieldGeometry heightfieldGeometry;
|
||||
shape->getHeightFieldGeometry(heightfieldGeometry);
|
||||
|
||||
physx::PxHeightField* heightfield = heightfieldGeometry.heightField;
|
||||
|
||||
int32_t numRows{ 0 };
|
||||
int32_t numColumns{ 0 };
|
||||
Physics::HeightfieldProviderRequestsBus::Event(
|
||||
gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows);
|
||||
EXPECT_EQ(numColumns, heightfield->getNbColumns());
|
||||
EXPECT_EQ(numRows, heightfield->getNbRows());
|
||||
|
||||
for (int sampleRow = 0; sampleRow < numRows; ++sampleRow)
|
||||
{
|
||||
for (int sampleColumn = 0; sampleColumn < numColumns; ++sampleColumn)
|
||||
{
|
||||
float minHeightBounds{ 0.0f };
|
||||
float maxHeightBounds{ 0.0f };
|
||||
Physics::HeightfieldProviderRequestsBus::Event(
|
||||
gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds,
|
||||
maxHeightBounds);
|
||||
|
||||
AZStd::vector<Physics::HeightMaterialPoint> samples;
|
||||
Physics::HeightfieldProviderRequestsBus::EventResult(
|
||||
samples, gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials);
|
||||
const float halfBounds{ (maxHeightBounds - minHeightBounds) / 2.0f };
|
||||
const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits<int16_t>::max() / halfBounds;
|
||||
|
||||
physx::PxHeightFieldSample samplePhysX = heightfield->getSample(sampleRow, sampleColumn);
|
||||
Physics::HeightMaterialPoint samplePhysics = samples[sampleRow * numColumns + sampleColumn];
|
||||
EXPECT_EQ(samplePhysX.height, azlossy_cast<physx::PxI16>(samplePhysics.m_height * scaleFactor));
|
||||
}
|
||||
}
|
||||
CleanupHeightfieldComponent();
|
||||
}
|
||||
|
||||
} // namespace PhysXEditorTests
|
||||
|
||||
@@ -18,6 +18,7 @@ set(FILES
|
||||
Tests/PolygonPrismMeshUtilsTest.cpp
|
||||
Tests/PhysXColliderComponentModeTests.cpp
|
||||
Tests/ShapeColliderComponentTests.cpp
|
||||
Tests/EditorHeightfieldColliderComponentTests.cpp
|
||||
Tests/TestColliderComponent.h
|
||||
Tests/SystemComponentTest.cpp
|
||||
Tests/RigidBodyComponentTests.cpp
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h
|
||||
)
|
||||
@@ -34,7 +34,8 @@ namespace UnitTest
|
||||
|
||||
MOCK_METHOD1(RegisterArea, void(AZ::EntityId areaId));
|
||||
MOCK_METHOD1(UnregisterArea, void(AZ::EntityId areaId));
|
||||
MOCK_METHOD1(RefreshArea, void(AZ::EntityId areaId));
|
||||
MOCK_METHOD2(RefreshArea,
|
||||
void(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask));
|
||||
};
|
||||
|
||||
class MockTerrainDataNotificationListener : public AzFramework::Terrain::TerrainDataNotificationBus::Handler
|
||||
|
||||
@@ -119,7 +119,9 @@ namespace Terrain
|
||||
LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
// Since this height data will no longer exist, notify the terrain system to refresh the area.
|
||||
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
|
||||
TerrainSystemServiceRequestBus::Broadcast(
|
||||
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
|
||||
AzFramework::Terrain::TerrainDataNotifications::HeightData);
|
||||
}
|
||||
|
||||
bool TerrainHeightGradientListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
|
||||
@@ -176,7 +178,9 @@ namespace Terrain
|
||||
void TerrainHeightGradientListComponent::OnCompositionChanged()
|
||||
{
|
||||
RefreshMinMaxHeights();
|
||||
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
|
||||
TerrainSystemServiceRequestBus::Broadcast(
|
||||
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
|
||||
AzFramework::Terrain::TerrainDataNotifications::HeightData);
|
||||
}
|
||||
|
||||
void TerrainHeightGradientListComponent::RefreshMinMaxHeights()
|
||||
|
||||
@@ -157,6 +157,12 @@ namespace Terrain
|
||||
|
||||
void TerrainLayerSpawnerComponent::RefreshArea()
|
||||
{
|
||||
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
|
||||
using Terrain = AzFramework::Terrain::TerrainDataNotifications;
|
||||
|
||||
// Notify the terrain system that the entire layer has changed, so both height and surface data can be affected.
|
||||
TerrainSystemServiceRequestBus::Broadcast(
|
||||
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
|
||||
static_cast<Terrain::TerrainDataChangedMask>(Terrain::HeightData | Terrain::SurfaceData)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,9 @@ namespace Terrain
|
||||
|
||||
void TerrainSurfaceGradientListComponent::OnCompositionChanged()
|
||||
{
|
||||
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
|
||||
TerrainSystemServiceRequestBus::Broadcast(
|
||||
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
|
||||
AzFramework::Terrain::TerrainDataNotifications::SurfaceData);
|
||||
}
|
||||
|
||||
} // namespace Terrain
|
||||
|
||||
@@ -218,9 +218,11 @@ namespace Terrain
|
||||
|
||||
const AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter());
|
||||
|
||||
AZ::Vector2 queryResolution = AZ::Vector2(1.0f);
|
||||
AZ::Vector2 queryResolution2D = AZ::Vector2(1.0f);
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
|
||||
queryResolution2D, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
|
||||
// Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension.
|
||||
float queryResolution = queryResolution2D.GetX();
|
||||
|
||||
// Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes.
|
||||
m_areaData.m_rebuildSectors = m_areaData.m_rebuildSectors ||
|
||||
@@ -228,16 +230,11 @@ namespace Terrain
|
||||
m_areaData.m_terrainBounds.GetMin().GetY() != worldBounds.GetMin().GetY() ||
|
||||
m_areaData.m_terrainBounds.GetMax().GetX() != worldBounds.GetMax().GetX() ||
|
||||
m_areaData.m_terrainBounds.GetMax().GetY() != worldBounds.GetMax().GetY() ||
|
||||
m_areaData.m_sampleSpacing != queryResolution.GetX();
|
||||
m_areaData.m_sampleSpacing != queryResolution;
|
||||
|
||||
m_areaData.m_transform = transform;
|
||||
m_areaData.m_terrainBounds = worldBounds;
|
||||
m_areaData.m_heightmapImageWidth = aznumeric_cast<uint32_t>(worldBounds.GetXExtent() / queryResolution.GetX());
|
||||
m_areaData.m_heightmapImageHeight = aznumeric_cast<uint32_t>(worldBounds.GetYExtent() / queryResolution.GetY());
|
||||
m_areaData.m_updateWidth = aznumeric_cast<uint32_t>(m_dirtyRegion.GetXExtent() / queryResolution.GetX());
|
||||
m_areaData.m_updateHeight = aznumeric_cast<uint32_t>(m_dirtyRegion.GetYExtent() / queryResolution.GetY());
|
||||
// Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension.
|
||||
m_areaData.m_sampleSpacing = queryResolution.GetX();
|
||||
m_areaData.m_sampleSpacing = queryResolution;
|
||||
m_areaData.m_heightmapUpdated = true;
|
||||
}
|
||||
|
||||
@@ -778,32 +775,43 @@ namespace Terrain
|
||||
|
||||
void TerrainFeatureProcessor::UpdateTerrainData()
|
||||
{
|
||||
uint32_t width = m_areaData.m_updateWidth;
|
||||
uint32_t height = m_areaData.m_updateHeight;
|
||||
const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds;
|
||||
|
||||
const float queryResolution = m_areaData.m_sampleSpacing;
|
||||
const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds;
|
||||
|
||||
const AZ::RHI::Size worldSize = AZ::RHI::Size(m_areaData.m_heightmapImageWidth, m_areaData.m_heightmapImageHeight, 1);
|
||||
int32_t heightmapImageXStart = aznumeric_cast<int32_t>(AZStd::ceilf(worldBounds.GetMin().GetX() / queryResolution));
|
||||
int32_t heightmapImageXEnd = aznumeric_cast<int32_t>(AZStd::floorf(worldBounds.GetMax().GetX() / queryResolution)) + 1;
|
||||
int32_t heightmapImageYStart = aznumeric_cast<int32_t>(AZStd::ceilf(worldBounds.GetMin().GetY() / queryResolution));
|
||||
int32_t heightmapImageYEnd = aznumeric_cast<int32_t>(AZStd::floorf(worldBounds.GetMax().GetY() / queryResolution)) + 1;
|
||||
uint32_t heightmapImageWidth = heightmapImageXEnd - heightmapImageXStart;
|
||||
uint32_t heightmapImageHeight = heightmapImageYEnd - heightmapImageYStart;
|
||||
|
||||
if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != worldSize)
|
||||
const AZ::RHI::Size heightmapSize = AZ::RHI::Size(heightmapImageWidth, heightmapImageHeight, 1);
|
||||
|
||||
if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != heightmapSize)
|
||||
{
|
||||
// World size changed, so the whole world needs updating.
|
||||
width = worldSize.m_width;
|
||||
height = worldSize.m_height;
|
||||
m_dirtyRegion = worldBounds;
|
||||
|
||||
const AZ::Data::Instance<AZ::RPI::AttachmentImagePool> imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool();
|
||||
AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(
|
||||
AZ::RHI::ImageBindFlags::ShaderRead, width, height, AZ::RHI::Format::R16_UNORM
|
||||
AZ::RHI::ImageBindFlags::ShaderRead, heightmapSize.m_width, heightmapSize.m_height, AZ::RHI::Format::R16_UNORM
|
||||
);
|
||||
|
||||
const AZ::Name TerrainHeightmapName = AZ::Name(TerrainHeightmapChars);
|
||||
m_areaData.m_heightmapImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainHeightmapName, nullptr, nullptr);
|
||||
AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image.");
|
||||
|
||||
// World size changed, so the whole height map needs updating.
|
||||
m_dirtyRegion = worldBounds;
|
||||
}
|
||||
|
||||
int32_t xStart = aznumeric_cast<int32_t>(AZStd::ceilf(m_dirtyRegion.GetMin().GetX() / queryResolution));
|
||||
int32_t xEnd = aznumeric_cast<int32_t>(AZStd::floorf(m_dirtyRegion.GetMax().GetX() / queryResolution)) + 1;
|
||||
int32_t yStart = aznumeric_cast<int32_t>(AZStd::ceilf(m_dirtyRegion.GetMin().GetY() / queryResolution));
|
||||
int32_t yEnd = aznumeric_cast<int32_t>(AZStd::floorf(m_dirtyRegion.GetMax().GetY() / queryResolution)) + 1;
|
||||
uint32_t updateWidth = xEnd - xStart;
|
||||
uint32_t updateHeight = yEnd - yStart;
|
||||
|
||||
AZStd::vector<uint16_t> pixels;
|
||||
pixels.reserve(width * height);
|
||||
pixels.reserve(updateWidth * updateHeight);
|
||||
|
||||
{
|
||||
// Block other threads from accessing the surface data bus while we are in GetHeightFromFloats (which may call into the SurfaceData bus).
|
||||
@@ -815,18 +823,17 @@ namespace Terrain
|
||||
auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false);
|
||||
typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex);
|
||||
|
||||
for (uint32_t y = 0; y < height; y++)
|
||||
for (int32_t y = yStart; y < yEnd; y++)
|
||||
{
|
||||
for (uint32_t x = 0; x < width; x++)
|
||||
for (int32_t x = xStart; x < xEnd; x++)
|
||||
{
|
||||
bool terrainExists = true;
|
||||
float terrainHeight = 0.0f;
|
||||
float xPos = x * queryResolution;
|
||||
float yPos = y * queryResolution;
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats,
|
||||
(x * queryResolution) + m_dirtyRegion.GetMin().GetX(),
|
||||
(y * queryResolution) + m_dirtyRegion.GetMin().GetY(),
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT,
|
||||
&terrainExists);
|
||||
xPos, yPos, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists);
|
||||
|
||||
const float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f);
|
||||
const float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits<uint16_t>::max());
|
||||
@@ -839,16 +846,18 @@ namespace Terrain
|
||||
|
||||
if (m_areaData.m_heightmapImage)
|
||||
{
|
||||
const float left = (m_dirtyRegion.GetMin().GetX() - worldBounds.GetMin().GetX()) / queryResolution;
|
||||
const float top = (m_dirtyRegion.GetMin().GetY() - worldBounds.GetMin().GetY()) / queryResolution;
|
||||
constexpr uint32_t BytesPerPixel = sizeof(uint16_t);
|
||||
const float left = xStart - (worldBounds.GetMin().GetX() / queryResolution);
|
||||
const float top = yStart - (worldBounds.GetMin().GetY() / queryResolution);
|
||||
|
||||
AZ::RHI::ImageUpdateRequest imageUpdateRequest;
|
||||
imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast<uint32_t>(left);
|
||||
imageUpdateRequest.m_imageSubresourcePixelOffset.m_top = aznumeric_cast<uint32_t>(top);
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = width * sizeof(uint16_t);
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = width * height * sizeof(uint16_t);
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = height;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = width;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = height;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = updateWidth * BytesPerPixel;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = updateWidth * updateHeight * BytesPerPixel;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = updateHeight;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = updateWidth;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = updateHeight;
|
||||
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1;
|
||||
imageUpdateRequest.m_sourceData = pixels.data();
|
||||
imageUpdateRequest.m_image = m_areaData.m_heightmapImage->GetRHIImage();
|
||||
@@ -1106,6 +1115,12 @@ namespace Terrain
|
||||
m_areaData.m_heightmapUpdated = false;
|
||||
m_areaData.m_macroMaterialsUpdated = false;
|
||||
|
||||
AZStd::array<float, 2> uvStep =
|
||||
{
|
||||
1.0f / aznumeric_cast<uint32_t>(m_areaData.m_terrainBounds.GetXExtent() / m_areaData.m_sampleSpacing),
|
||||
1.0f / aznumeric_cast<uint32_t>(m_areaData.m_terrainBounds.GetYExtent() / m_areaData.m_sampleSpacing),
|
||||
};
|
||||
|
||||
for (SectorData& sectorData : m_sectorData)
|
||||
{
|
||||
ShaderTerrainData terrainDataForSrg;
|
||||
@@ -1123,11 +1138,7 @@ namespace Terrain
|
||||
((yPatch + GridMeters) - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent()
|
||||
};
|
||||
|
||||
terrainDataForSrg.m_uvStep =
|
||||
{
|
||||
1.0f / m_areaData.m_heightmapImageWidth,
|
||||
1.0f / m_areaData.m_heightmapImageHeight,
|
||||
};
|
||||
terrainDataForSrg.m_uvStep = uvStep;
|
||||
|
||||
AZ::Transform transform = m_areaData.m_transform;
|
||||
transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ());
|
||||
|
||||
@@ -303,10 +303,6 @@ namespace Terrain
|
||||
AZ::Transform m_transform{ AZ::Transform::CreateIdentity() };
|
||||
AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() };
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> m_heightmapImage;
|
||||
uint32_t m_heightmapImageWidth{ 0 };
|
||||
uint32_t m_heightmapImageHeight{ 0 };
|
||||
uint32_t m_updateWidth{ 0 };
|
||||
uint32_t m_updateHeight{ 0 };
|
||||
float m_sampleSpacing{ 0.0f };
|
||||
bool m_heightmapUpdated{ true };
|
||||
bool m_macroMaterialsUpdated{ true };
|
||||
|
||||
@@ -76,6 +76,7 @@ void TerrainSystem::Activate()
|
||||
m_dirtyRegion = AZ::Aabb::CreateNull();
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSettingsDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
m_requestedSettings.m_systemActive = true;
|
||||
|
||||
{
|
||||
@@ -115,6 +116,7 @@ void TerrainSystem::Deactivate()
|
||||
m_dirtyRegion = AZ::Aabb::CreateNull();
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSettingsDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
m_requestedSettings.m_systemActive = false;
|
||||
|
||||
AzFramework::Terrain::TerrainDataNotificationBus::Broadcast(
|
||||
@@ -549,6 +551,7 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId)
|
||||
m_registeredAreas[areaId] = aabb;
|
||||
m_dirtyRegion.AddAabb(aabb);
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
}
|
||||
|
||||
void TerrainSystem::UnregisterArea(AZ::EntityId areaId)
|
||||
@@ -567,14 +570,17 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId)
|
||||
{
|
||||
m_dirtyRegion.AddAabb(aabb);
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void TerrainSystem::RefreshArea(AZ::EntityId areaId)
|
||||
void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask)
|
||||
{
|
||||
using Terrain = AzFramework::Terrain::TerrainDataNotifications;
|
||||
|
||||
AZStd::unique_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
auto areaAabb = m_registeredAreas.find(areaId);
|
||||
@@ -588,11 +594,18 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId)
|
||||
expandedAabb.AddAabb(newAabb);
|
||||
|
||||
m_dirtyRegion.AddAabb(expandedAabb);
|
||||
m_terrainHeightDirty = true;
|
||||
|
||||
// Keep track of which types of data have changed so that we can send out the appropriate notifications later.
|
||||
|
||||
m_terrainHeightDirty = m_terrainHeightDirty || ((changeMask & Terrain::HeightData) == Terrain::HeightData);
|
||||
|
||||
m_terrainSurfacesDirty = m_terrainSurfacesDirty || ((changeMask & Terrain::SurfaceData) == Terrain::SurfaceData);
|
||||
}
|
||||
|
||||
void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
||||
{
|
||||
using Terrain = AzFramework::Terrain::TerrainDataNotifications;
|
||||
|
||||
bool terrainSettingsChanged = false;
|
||||
|
||||
if (m_terrainSettingsDirty)
|
||||
@@ -607,6 +620,7 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
||||
m_dirtyRegion = m_currentSettings.m_worldBounds;
|
||||
m_dirtyRegion.AddAabb(m_requestedSettings.m_worldBounds);
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
m_currentSettings.m_worldBounds = m_requestedSettings.m_worldBounds;
|
||||
}
|
||||
|
||||
@@ -614,12 +628,13 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
||||
{
|
||||
m_dirtyRegion = AZ::Aabb::CreateNull();
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
}
|
||||
|
||||
m_currentSettings = m_requestedSettings;
|
||||
}
|
||||
|
||||
if (terrainSettingsChanged || m_terrainHeightDirty)
|
||||
if (terrainSettingsChanged || m_terrainHeightDirty || m_terrainSurfacesDirty)
|
||||
{
|
||||
// Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus).
|
||||
// We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions
|
||||
@@ -629,24 +644,27 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
||||
auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false);
|
||||
typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex);
|
||||
|
||||
AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask =
|
||||
AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::None;
|
||||
Terrain::TerrainDataChangedMask changeMask = Terrain::TerrainDataChangedMask::None;
|
||||
|
||||
if (terrainSettingsChanged)
|
||||
{
|
||||
changeMask = static_cast<AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask>(
|
||||
changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::Settings);
|
||||
changeMask = static_cast<Terrain::TerrainDataChangedMask>(changeMask | Terrain::TerrainDataChangedMask::Settings);
|
||||
}
|
||||
if (m_terrainHeightDirty)
|
||||
{
|
||||
changeMask = static_cast<AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask>(
|
||||
changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::HeightData);
|
||||
changeMask = static_cast<Terrain::TerrainDataChangedMask>(changeMask | Terrain::TerrainDataChangedMask::HeightData);
|
||||
}
|
||||
|
||||
if (m_terrainSurfacesDirty)
|
||||
{
|
||||
changeMask = static_cast<Terrain::TerrainDataChangedMask>(changeMask | Terrain::TerrainDataChangedMask::SurfaceData);
|
||||
}
|
||||
|
||||
// Make sure to set these *before* calling OnTerrainDataChanged, since it's possible that subsystems reacting to that call will
|
||||
// cause the data to become dirty again.
|
||||
AZ::Aabb dirtyRegion = m_dirtyRegion;
|
||||
m_terrainHeightDirty = false;
|
||||
m_terrainSurfacesDirty = false;
|
||||
m_dirtyRegion = AZ::Aabb::CreateNull();
|
||||
|
||||
AzFramework::Terrain::TerrainDataNotificationBus::Broadcast(
|
||||
|
||||
@@ -47,7 +47,8 @@ namespace Terrain
|
||||
|
||||
void RegisterArea(AZ::EntityId areaId) override;
|
||||
void UnregisterArea(AZ::EntityId areaId) override;
|
||||
void RefreshArea(AZ::EntityId areaId) override;
|
||||
void RefreshArea(
|
||||
AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) override;
|
||||
|
||||
///////////////////////////////////////////
|
||||
// TerrainDataRequestBus::Handler Impl
|
||||
@@ -164,6 +165,7 @@ namespace Terrain
|
||||
|
||||
bool m_terrainSettingsDirty = true;
|
||||
bool m_terrainHeightDirty = false;
|
||||
bool m_terrainSurfacesDirty = false;
|
||||
AZ::Aabb m_dirtyRegion;
|
||||
|
||||
mutable AZStd::shared_mutex m_areaMutex;
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Terrain
|
||||
// register an area to override terrain
|
||||
virtual void RegisterArea(AZ::EntityId areaId) = 0;
|
||||
virtual void UnregisterArea(AZ::EntityId areaId) = 0;
|
||||
virtual void RefreshArea(AZ::EntityId areaId) = 0;
|
||||
virtual void RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) = 0;
|
||||
};
|
||||
|
||||
using TerrainSystemServiceRequestBus = AZ::EBus<TerrainSystemServiceRequests>;
|
||||
|
||||
@@ -190,7 +190,7 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerTransformChangedUpdatesTerrainSyst
|
||||
CreateMockTerrainSystem();
|
||||
|
||||
// The TransformChanged call should refresh the area.
|
||||
EXPECT_CALL(*m_terrainSystem, RefreshArea(_)).Times(1);
|
||||
EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1);
|
||||
|
||||
AddLayerSpawnerAndShapeComponentToEntity();
|
||||
|
||||
@@ -211,7 +211,7 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerShapeChangedUpdatesTerrainSystem)
|
||||
CreateMockTerrainSystem();
|
||||
|
||||
// The ShapeChanged call should refresh the area.
|
||||
EXPECT_CALL(*m_terrainSystem, RefreshArea(_)).Times(1);
|
||||
EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1);
|
||||
|
||||
AddLayerSpawnerAndShapeComponentToEntity();
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientRefreshesTer
|
||||
// As the TerrainHeightGradientListComponent subscribes to the dependency monitor, RefreshArea will be called twice:
|
||||
// once due to OnCompositionChanged being picked up by the the dependency monitor and resending the notification,
|
||||
// and once when the HeightGradientListComponent gets the OnCompositionChanged directly through the DependencyNotificationBus.
|
||||
EXPECT_CALL(terrainSystem, RefreshArea(_)).Times(2);
|
||||
EXPECT_CALL(terrainSystem, RefreshArea(_, _)).Times(2);
|
||||
|
||||
LmbrCentral::DependencyNotificationBus::Event(m_entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ endif()
|
||||
|
||||
ly_install_directory(DIRECTORIES .)
|
||||
|
||||
ly_install_directory(DIRECTORIES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/Registry
|
||||
DESTINATION ${runtime_output_directory}
|
||||
)
|
||||
foreach(conf IN LISTS CMAKE_CONFIGURATION_TYPES)
|
||||
string(TOUPPER ${conf} UCONF)
|
||||
string(REPLACE "$<CONFIG>" "${conf}" output ${runtime_output_directory})
|
||||
ly_install_directory(DIRECTORIES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${conf}/Registry
|
||||
DESTINATION ${output}
|
||||
COMPONENT ${LY_INSTALL_PERMUTATION_COMPONENT}_${UCONF}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
ly_install_directory(
|
||||
DIRECTORIES
|
||||
AssetGem
|
||||
CustomTool
|
||||
PythonGem
|
||||
CppToolGem
|
||||
PythonToolGem
|
||||
DefaultGem
|
||||
DefaultProject
|
||||
MinimalProject
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user