Merge branch 'development' of https://github.com/aws-lumberyard-dev/o3de into scripting/rte_issues

This commit is contained in:
lsemp3d
2021-10-05 16:35:30 -07:00
353 changed files with 4554 additions and 2149 deletions
@@ -64,7 +64,7 @@ namespace AWSCore
void AWSCoreConfiguration::InitSourceProjectFolderPath()
{
auto sourceProjectFolder = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
auto sourceProjectFolder = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@");
if (!sourceProjectFolder)
{
AZ_Error(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage);
@@ -197,7 +197,7 @@ namespace AWSCore
subMenu->addAction(AddExternalLinkAction(
AWSMetricsAdvancedTopicsActionText, AWSMetricsAdvancedTopicsUrl, ":/Notifications/link.svg"));
AZStd::string priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devroot@");
AZStd::string priorAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@engroot@");
AZStd::string configFilePath = priorAlias + "\\Gems\\AWSMetrics\\Code\\" + AZ::SettingsRegistryInterface::RegistryFolder;
AzFramework::StringFunc::Path::Normalize(configFilePath);
@@ -60,7 +60,7 @@ public:
m_normalizedSourceProjectFolder.c_str(), AZ::SettingsRegistryInterface::RegistryFolder);
AzFramework::StringFunc::Path::Normalize(m_normalizedSetRegFolderPath);
m_localFileIO->SetAlias("@devassets@", m_normalizedSourceProjectFolder.c_str());
m_localFileIO->SetAlias("@projectroot@", m_normalizedSourceProjectFolder.c_str());
CreateTestSetRegFile(TEST_VALID_RESOURCE_MAPPING_SETREG);
}
@@ -122,7 +122,7 @@ private:
TEST_F(AWSCoreConfigurationTest, InitConfig_NoSourceProjectFolderFound_ReturnEmptyConfigFilePath)
{
m_settingsRegistry->MergeSettingsFile(m_normalizedSetRegFilePath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {});
m_localFileIO->ClearAlias("@devassets@");
m_localFileIO->ClearAlias("@projectroot@");
AZ_TEST_START_TRACE_SUPPRESSION;
m_awsCoreConfiguration->InitConfig();
@@ -154,7 +154,7 @@ TEST_F(AWSCoreConfigurationTest, InitConfig_LoadValidSettingsRegistry_ReturnNonE
TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_NoSourceProjectFolderFound_ReturnEmptyConfigFilePath)
{
m_settingsRegistry->MergeSettingsFile(m_normalizedSetRegFilePath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {});
m_localFileIO->ClearAlias("@devassets@");
m_localFileIO->ClearAlias("@projectroot@");
m_awsCoreConfiguration->ReloadConfiguration();
auto actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath();
@@ -32,7 +32,7 @@ class AWSCoreEditorMenuTest
{
AWSCoreEditorUIFixture::SetUp();
AWSCoreFixture::SetUp();
m_localFileIO->SetAlias("@devroot@", "dummy engine root");
m_localFileIO->SetAlias("@engroot@", "dummy engine root");
}
void TearDown() override
@@ -52,6 +52,7 @@ class AWSGameLift(core.Construct):
stack_name=stack_name,
fleet_configurations=fleet_configurations,
create_game_session_queue=self.node.try_get_context('create_game_session_queue') == 'true',
flex_match=self.node.try_get_context('flex_match') == 'true',
description=f'Contains resources for the AWS GameLift Gem stack as part of the {project_name} project',
tags=tags,
env=env
@@ -0,0 +1,6 @@
"""
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
"""
@@ -0,0 +1,30 @@
"""
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
"""
# Matchmaking rule formatted as a JSON string.
# Comments are not allowed in JSON, but most elements support a description field.
# For instructions on designing Matchmaking rule sets, please check:
# https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-design-ruleset.html
RULE_SET_BODY = '{"ruleLanguageVersion":"1.0","teams":[{"name":"Players","maxPlayers":4,"minPlayers":2}]}'
# A flag that determines whether a match that was created with this configuration
# must be accepted by the matched players.
ACCEPTANCE_REQUIRED = False
# The maximum duration, in seconds, that a matchmaking ticket can remain in process before timing out.
# Requests that fail due to timing out can be resubmitted as needed.
REQUEST_TIMEOUT_SECONDS = 300
# The number of player slots in a match to keep open for future players.
# This parameter is not used if FlexMatchMode is set to STANDALONE.
ADDITIONAL_PLAYER_COUNT = 2
# The method used to backfill game sessions that are created with this matchmaking configuration.
# Specify MANUAL when your game manages backfill requests manually or does not use the match backfill feature.
# Specify AUTOMATIC to have GameLift create a StartMatchBackfill request whenever a game session has one or more
# open slots.
BACKFILL_MODE = 'AUTOMATIC'
@@ -0,0 +1,60 @@
"""
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
"""
import typing
from aws_cdk import (
core,
aws_gamelift as gamelift
)
from . import flexmatch_configurations
FLEX_MATCH_MODE = 'WITH_QUEUE'
class MatchmakingResoures:
"""
Create a matchmaking rule set and matchmaking configuration for Gamelift FlexMatch.
For more information about Gamelift FlexMatch, please check
https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-intro.html
"""
def __init__(self, stack: core.Stack, game_session_queue_arns: typing.List[str]):
rule_set = gamelift.CfnMatchmakingRuleSet(
scope=stack,
id='MatchmakingRuleSet',
name=f'{stack.stack_name}-MatchmakingRuleSet',
rule_set_body=flexmatch_configurations.RULE_SET_BODY
)
matchmaking_configuration = gamelift.CfnMatchmakingConfiguration(
scope=stack,
id='MatchmakingConfiguration',
acceptance_required=flexmatch_configurations.ACCEPTANCE_REQUIRED,
name=f'{stack.stack_name}-MatchmakingConfiguration',
request_timeout_seconds=flexmatch_configurations.REQUEST_TIMEOUT_SECONDS,
rule_set_name=rule_set.name,
additional_player_count=flexmatch_configurations.ADDITIONAL_PLAYER_COUNT,
backfill_mode=flexmatch_configurations.BACKFILL_MODE,
flex_match_mode=FLEX_MATCH_MODE,
game_session_queue_arns=game_session_queue_arns if len(game_session_queue_arns) else None
)
matchmaking_configuration.node.add_dependency(rule_set)
# Export the matchmaking rule set and configuration names as stack outputs
core.CfnOutput(
stack,
id='MatchmakingRuleSetName',
description='Name of the matchmaking rule set',
export_name=f'{stack.stack_name}:MatchmakingRuleSet',
value=rule_set.name)
core.CfnOutput(
stack,
id='MatchmakingConfigurationName',
description='Name of the matchmaking configuration',
export_name=f'{stack.stack_name}:MatchmakingConfiguration',
value=matchmaking_configuration.name)
@@ -0,0 +1,6 @@
"""
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
"""
@@ -0,0 +1,43 @@
"""
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
"""
import typing
from aws_cdk import (
core,
aws_gamelift as gamelift
)
class GameSessionQueueResources:
"""
Create a game session queue which fulfills game session placement requests using the fleets.
For more information about Gamelift game session queues, please check
https://docs.aws.amazon.com/gamelift/latest/developerguide/queues-intro.html
"""
def __init__(self, stack: core.Stack, destinations: typing.List):
self._game_session_queue = gamelift.CfnGameSessionQueue(
scope=stack,
id=f'{stack.stack_name}-GameLiftQueue',
name=f'{stack.stack_name}-GameLiftQueue',
destinations=[
gamelift.CfnGameSessionQueue.DestinationProperty(
destination_arn=resource_arn
) for resource_arn in destinations
]
)
# Export the game session queue name as a stack output
core.CfnOutput(
scope=stack,
id='GameSessionQueue',
description='Name of the game session queue',
export_name=f'{stack.stack_name}:GameSessionQueue',
value=self._game_session_queue.name)
@property
def game_session_queue_arn(self) -> str:
return self._game_session_queue.attr_arn
@@ -5,10 +5,13 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import typing
from aws_cdk import (
core,
aws_gamelift as gamelift
)
from aws_cdk import core
from aws_cdk import aws_gamelift as gamelift
from .flexmatch import matchmaking
from .game_session_queue import game_session_queue
class GameLiftStack(core.Stack):
@@ -19,7 +22,9 @@ class GameLiftStack(core.Stack):
"""
def __init__(self, scope: core.Construct, id_: str,
stack_name: str, fleet_configurations: dict,
create_game_session_queue: bool, **kwargs) -> None:
create_game_session_queue: bool,
flex_match: bool,
**kwargs) -> None:
super().__init__(scope, id_, **kwargs)
self._stack_name = stack_name
@@ -50,7 +55,7 @@ class GameLiftStack(core.Stack):
queue_destinations.append(destination_arn)
# Export the GameLift fleet ids as a stack output
fleets_output = core.CfnOutput(
core.CfnOutput(
self,
id='GameLiftFleets',
description='List of GameLift fleet ids',
@@ -58,17 +63,13 @@ class GameLiftStack(core.Stack):
value=','.join(fleet_ids)
)
if create_game_session_queue:
# Create a game session queue which fulfills game session placement requests using the fleets
game_session_queue = self._create_game_session_queue(queue_destinations)
game_session_queue_arns = []
if flex_match or create_game_session_queue:
queue = game_session_queue.GameSessionQueueResources(self, queue_destinations)
game_session_queue_arns.append(queue.game_session_queue_arn)
# Export the game session queue name as a stack output
game_session_queue_output = core.CfnOutput(
self,
id='GameSessionQueue',
description='Name of the game session queue',
export_name=f'{self._stack_name}:GameSessionQueue',
value=game_session_queue.name)
if flex_match:
matchmaking.MatchmakingResoures(self, game_session_queue_arns)
def _create_fleet(self, fleet_configuration: dict, identifier: int) -> gamelift.CfnFleet:
"""
@@ -155,25 +156,3 @@ class GameLiftStack(core.Stack):
)
return alias
def _create_game_session_queue(self, destinations: typing.List) -> gamelift.CfnGameSessionQueue:
"""
Create a placement queue that processes requests for new game sessions.
:param destinations: Destinations of the queue.
:return: Generated GameLift game session queue.
"""
game_session_queue = gamelift.CfnGameSessionQueue(
self,
id=f'{self._stack_name}-GameLiftQueue',
name=f'{self._stack_name}-game-session-queue',
destinations=[
gamelift.CfnGameSessionQueue.DestinationProperty(
destination_arn=resource_arn
) for resource_arn in destinations
]
)
return game_session_queue
@@ -22,7 +22,7 @@ namespace AWSMetrics
AZStd::string IdentityProvider::GetEngineVersion()
{
static constexpr const char* EngineConfigFilePath = "@root@/engine.json";
static constexpr const char* EngineConfigFilePath = "@products@/engine.json";
static constexpr const char* EngineVersionJsonKey = "O3DEVersion";
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetDirectInstance();
+28 -12
View File
@@ -16,8 +16,8 @@
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace AWSMetrics
{
@@ -34,15 +34,22 @@ namespace AWSMetrics
// Set up the file IO and alias
m_localFileIO = aznew AZ::IO::LocalFileIO();
m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
// we need to set it to nullptr first because otherwise the
// we need to set it to nullptr first because otherwise the
// underneath code assumes that we might be leaking the previous instance
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_localFileIO);
const AZStd::string engineRoot = AZ::Test::GetEngineRootPath();
m_localFileIO->SetAlias("@devroot@", engineRoot.c_str());
m_localFileIO->SetAlias("@root@", engineRoot.c_str());
m_localFileIO->SetAlias("@user@", GetTestFolderPath().c_str());
const AZ::IO::Path engineRoot = AZ::Test::GetEngineRootPath();
const auto productAssetPath = GetTestFolderPath() / "Cache";
const auto userPath = GetTestFolderPath() / "user";
m_localFileIO->CreatePath(productAssetPath.c_str());
m_localFileIO->CreatePath(userPath.c_str());
m_localFileIO->SetAlias("@engroot@", engineRoot.c_str());
m_localFileIO->SetAlias("@products@", productAssetPath.c_str());
m_localFileIO->SetAlias("@user@", userPath.c_str());
// Copy engine.json to the cache
EXPECT_TRUE(m_localFileIO->Copy((engineRoot / "engine.json").c_str(), "engine.json"));
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
m_registrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
@@ -69,6 +76,13 @@ namespace AWSMetrics
m_serializeContext.reset();
m_registrationContext.reset();
const auto productAssetPath = GetTestFolderPath() / "Cache";
const auto userPath = GetTestFolderPath() / "user";
// Clear the product asset cache alias to prevent cache write errors
m_localFileIO->ClearAlias("@products@");
m_localFileIO->DestroyPath(userPath.c_str());
m_localFileIO->DestroyPath(productAssetPath.c_str());
AZ::IO::FileIOBase::SetInstance(nullptr);
delete m_localFileIO;
AZ::IO::FileIOBase::SetInstance(m_priorFileIO);
@@ -97,22 +111,22 @@ namespace AWSMetrics
bool CreateFile(const AZStd::string& filePath, const AZStd::string& content)
{
AZ::IO::HandleType fileHandle;
// Suppress errors about writing to product asset cache
AZ_TEST_START_TRACE_SUPPRESSION;
if (!m_localFileIO->Open(filePath.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText, fileHandle))
{
return false;
}
m_localFileIO->Write(fileHandle, content.c_str(), content.size());
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
m_localFileIO->Close(fileHandle);
return true;
}
AZStd::string GetDefaultTestFilePath()
{
AZStd::string testFilePath = GetTestFolderPath();
AzFramework::StringFunc::Path::Join(testFilePath.c_str(), "Test.json", testFilePath);
return testFilePath;
return (GetTestFolderPath() / "Test.json").Native();
}
bool RemoveFile(const AZStd::string& filePath)
@@ -133,14 +147,16 @@ namespace AWSMetrics
AZ::IO::FileIOBase* m_priorFileIO = nullptr;
AZ::IO::FileIOBase* m_localFileIO = nullptr;
AZ::Test::ScopedAutoTempDirectory m_testDirectory;
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
AZStd::unique_ptr<AZ::JsonRegistrationContext> m_registrationContext;
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_settingsRegistry;
private:
AZStd::string GetTestFolderPath()
AZ::IO::Path GetTestFolderPath()
{
return AZ_TRAIT_TEST_ROOT_FOLDER;
AZ::IO::Path testPathString{ m_testDirectory.GetDirectory() };
return testPathString;
}
};
}
@@ -142,14 +142,14 @@ namespace AssetValidation
system.GetIConsole()->AddCommand("addseedlist", ConsoleCommandAddSeedList);
system.GetIConsole()->AddCommand("removeseedlist", ConsoleCommandRemoveSeedList);
system.GetIConsole()->AddCommand("printexcluded", ConsoleCommandTogglePrintExcluded);
}
}
bool AssetValidationSystemComponent::IsKnownAsset(const char* assetPath)
{
AZStd::string lowerAsset{ assetPath };
AZStd::replace(lowerAsset.begin(), lowerAsset.end(), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR);
const AZStd::vector<AZStd::string> prefixes = { "./", "@assets@/" };
const AZStd::vector<AZStd::string> prefixes = { "./", "@products@/" };
for (const AZStd::string& prefix : prefixes)
{
if (lowerAsset.starts_with(prefix))
@@ -392,7 +392,7 @@ namespace AssetValidation
AssetValidationRequestBus::Broadcast(&AssetValidationRequestBus::Events::AddSeedList, seedfilepath);
}
bool AssetValidationSystemComponent::AddSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId)
bool AssetValidationSystemComponent::AddSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId)
{
for (const AzFramework::SeedInfo& thisSeed : seedList)
{
@@ -401,7 +401,7 @@ namespace AssetValidation
return true;
}
bool AssetValidationSystemComponent::RemoveSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId)
bool AssetValidationSystemComponent::RemoveSeedsFor(const AzFramework::AssetSeedList& seedList, AZ::u32 seedId)
{
AssetValidationRequests::AssetSourceList removeList;
for (const AzFramework::SeedInfo& thisSeed : seedList)
@@ -244,7 +244,7 @@ namespace ImageProcessingAtom
}
AZ::IO::FixedMaxPath projectConfigFolder;
if (auto sourceGameRoot = fileIoBase->ResolvePath("@devassets@"); sourceGameRoot.has_value())
if (auto sourceGameRoot = fileIoBase->ResolvePath("@projectroot@"); sourceGameRoot.has_value())
{
projectConfigFolder = *sourceGameRoot;
projectConfigFolder /= s_projectConfigRelativeFolder;
@@ -139,16 +139,16 @@ namespace AZ
//! Validates if a given .shadervariantlist file is located at the correct path for a given .shader full path.
//! There are two valid paths:
//! 1- Lower Precedence: The same folder where the .shader file is located.
//! 2- Higher Precedence: <DEVROOT>/<GAME>/ShaderVariants/<Same Scan Folder Subpath as the .shader file>.
//! 2- Higher Precedence: <project-path>/ShaderVariants/<Same Scan Folder Subpath as the .shader file>.
//! The "Higher Precedence" path gives the option to game projects to override what variants to generate. If this
//! file exists then the "Lower Precedence" path is disregarded.
//! A .shader full path is located under an AP scan folder.
//! Example: "<DEVROOT>/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shader"
//! - In this example the Scan Folder is "<DEVROOT>/Gems/Atom/Feature/Common/Assets", while the subfolder is "Materials/Types".
//! Example: "<atom-gem-path>/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shader"
//! - In this example the Scan Folder is "<atom-gem-path>/Gems/Atom/Feature/Common/Assets", while the subfolder is "Materials/Types".
//! The "Higher Precedence" expected valid location for the .shadervariantlist would be:
//! - <DEVROOT>/<GameProject>/ShaderVariants/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
//! - <atom-gem-path>/<GameProject>/ShaderVariants/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
//! The "Lower Precedence" valid location would be:
//! - <DEVROOT>/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
//! - <atom-gem-path>/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.shadervariantlist.
//! @shouldExitEarlyFromProcessJob [out] Set to true if ProcessJob should do no work but return successfully.
//! Set to false if ProcessJob should do work and create assets.
//! When @shaderVariantListFileFullPath is provided by a Gem/Feature instead of the Game Project
@@ -169,17 +169,13 @@ namespace AZ
AZStd::string shaderVariantListFileRelativePath = shaderProductFileRelativePath;
AzFramework::StringFunc::Path::ReplaceExtension(shaderVariantListFileRelativePath, RPI::ShaderVariantListSourceData::Extension);
const char * gameProjectPath = nullptr;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameProjectPath, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath);
AZ::IO::FixedMaxPath gameProjectPath = AZ::Utils::GetProjectPath();
AZStd::string expectedHigherPrecedenceFileFullPath;
AzFramework::StringFunc::Path::Join(gameProjectPath, RPI::ShaderVariantTreeAsset::CommonSubFolder, expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */);
AzFramework::StringFunc::Path::Join(expectedHigherPrecedenceFileFullPath.c_str(), shaderProductFileRelativePath.c_str(), expectedHigherPrecedenceFileFullPath, false /* handle directory overlap? */, false /* be case insensitive? */);
AzFramework::StringFunc::Path::ReplaceExtension(expectedHigherPrecedenceFileFullPath, AZ::RPI::ShaderVariantListSourceData::Extension);
AzFramework::StringFunc::Path::Normalize(expectedHigherPrecedenceFileFullPath);
auto expectedHigherPrecedenceFileFullPath = (gameProjectPath
/ RPI::ShaderVariantTreeAsset::CommonSubFolder / shaderProductFileRelativePath).LexicallyNormal();
expectedHigherPrecedenceFileFullPath.ReplaceExtension(AZ::RPI::ShaderVariantListSourceData::Extension);
AZStd::string normalizedShaderVariantListFileFullPath = shaderVariantListFileFullPath;
AzFramework::StringFunc::Path::Normalize(normalizedShaderVariantListFileFullPath);
auto normalizedShaderVariantListFileFullPath = AZ::IO::FixedMaxPath(shaderVariantListFileFullPath).LexicallyNormal();
if (expectedHigherPrecedenceFileFullPath == normalizedShaderVariantListFileFullPath)
{
@@ -203,23 +199,15 @@ namespace AZ
}
// Check the "Lower Precedence" case, .shader path == .shadervariantlist path.
AZStd::string normalizedShaderFileFullPath = shaderFileFullPath;
AzFramework::StringFunc::Path::Normalize(normalizedShaderFileFullPath);
AZ::IO::Path normalizedShaderFileFullPath = AZ::IO::Path(shaderFileFullPath).LexicallyNormal();
AZStd::string normalizedShaderFileFullPathWithoutExtension = normalizedShaderFileFullPath;
AzFramework::StringFunc::Path::StripExtension(normalizedShaderFileFullPathWithoutExtension);
auto normalizedShaderFileFullPathWithoutExtension = normalizedShaderFileFullPath;
normalizedShaderFileFullPathWithoutExtension.ReplaceExtension("");
AZStd::string normalizedShaderVariantListFileFullPathWithoutExtension = normalizedShaderVariantListFileFullPath;
AzFramework::StringFunc::Path::StripExtension(normalizedShaderVariantListFileFullPathWithoutExtension);
auto normalizedShaderVariantListFileFullPathWithoutExtension = normalizedShaderVariantListFileFullPath;
normalizedShaderVariantListFileFullPathWithoutExtension.ReplaceExtension("");
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
//In certain circumstances, the capitalization of the drive letter may not match
const bool caseSensitive = false;
#else
//On the other platforms there's no drive letter, so it should be a non-issue.
const bool caseSensitive = true;
#endif
if (!StringFunc::Equal(normalizedShaderFileFullPathWithoutExtension.c_str(), normalizedShaderVariantListFileFullPathWithoutExtension.c_str(), caseSensitive))
if (normalizedShaderFileFullPathWithoutExtension != normalizedShaderVariantListFileFullPathWithoutExtension)
{
AZ_Error(ShaderVariantAssetBuilderName, false, "For shader file at path [%s], the shader variant list [%s] is expected to be located at [%s.%s] or [%s]"
, normalizedShaderFileFullPath.c_str(), normalizedShaderVariantListFileFullPath.c_str(),
@@ -14,10 +14,14 @@
#include <Atom/RPI.Public/Shader/ShaderSystemInterface.h>
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <Atom/Feature/TransformService/TransformServiceFeatureProcessor.h>
#include <Atom/Feature/Mesh/ModelReloaderSystemInterface.h>
#include <RayTracing/RayTracingFeatureProcessor.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AtomCore/std/parallel/concurrency_checker.h>
#include <AzCore/Console/Console.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzCore/Component/TickBus.h>
namespace AZ
{
@@ -38,6 +42,7 @@ namespace AZ
private:
class MeshLoader
: private Data::AssetBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
{
public:
using ModelChangedEvent = MeshFeatureProcessorInterface::ModelChangedEvent;
@@ -52,6 +57,15 @@ namespace AZ
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
void OnAssetError(Data::Asset<Data::AssetData> asset) override;
// AssetCatalogEventBus::Handler overrides...
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetAdded(const AZ::Data::AssetId& assetId) override;
void OnModelReloaded(Data::Asset<Data::AssetData> asset);
ModelReloadedEvent::Handler m_modelReloadedEventHandler { [&](Data::Asset<RPI::ModelAsset> modelAsset)
{
OnModelReloaded(modelAsset);
} };
MeshFeatureProcessorInterface::ModelChangedEvent m_modelChangedEvent;
Data::Asset<RPI::ModelAsset> m_modelAsset;
MeshDataInstance* m_parent = nullptr;
@@ -61,6 +75,7 @@ namespace AZ
void Init(Data::Instance<RPI::Model> model);
void BuildDrawPacketList(size_t modelLodIndex);
void SetRayTracingData();
void RemoveRayTracingData();
void SetSortKey(RHI::DrawItemSortKey sortKey);
RHI::DrawItemSortKey GetSortKey() const;
void SetMeshLodConfiguration(RPI::Cullable::LodConfiguration meshLodConfig);
@@ -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
*
*/
#pragma once
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/RTTI/RTTI.h>
namespace AZ
{
namespace Data
{
template<class T>
class Asset;
}
namespace Render
{
using ModelReloadedEvent = Event<const Data::Asset<RPI::ModelAsset>>;
//! A system that handles reloading the hierarchy of model assets in the correct order
class ModelReloaderSystemInterface
{
public:
AZ_RTTI(AZ::Render::ModelReloaderSystemInterface, "{E7E05B1F-8928-4A1B-B75D-3D5433E65BCA}");
ModelReloaderSystemInterface()
{
Interface<ModelReloaderSystemInterface>::Register(this);
}
virtual ~ModelReloaderSystemInterface()
{
Interface<ModelReloaderSystemInterface>::Unregister(this);
}
static ModelReloaderSystemInterface* Get()
{
return Interface<ModelReloaderSystemInterface>::Get();
}
//! Requests a model reload and passes in a callback event handler for when the reload is finished
virtual void ReloadModel(
Data::Asset<RPI::ModelAsset> modelAsset, ModelReloadedEvent::Handler& onReloadedEventHandler) = 0;
// Note that you have to delete these for safety reasons, you will trip a static_assert if you do not
AZ_DISABLE_COPY_MOVE(ModelReloaderSystemInterface);
};
} // namespace Render
} // namespace AZ
@@ -103,11 +103,15 @@
#include <ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.h>
#include <ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h>
#include <OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h>
#include <Mesh/ModelReloaderSystem.h>
namespace AZ
{
namespace Render
{
CommonSystemComponent::CommonSystemComponent() = default;
CommonSystemComponent::~CommonSystemComponent() = default;
void CommonSystemComponent::Reflect(ReflectContext* context)
{
AuxGeomFeatureProcessor::Reflect(context);
@@ -292,10 +296,13 @@ namespace AZ
// setup handler for load pass template mappings
m_loadTemplatesHandler = RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler([this]() { this->LoadPassTemplateMappings(); });
RPI::PassSystemInterface::Get()->ConnectEvent(m_loadTemplatesHandler);
m_modelReloaderSystem = AZStd::make_unique<ModelReloaderSystem>();
}
void CommonSystemComponent::Deactivate()
{
m_modelReloaderSystem.reset();
m_loadTemplatesHandler.Disconnect();
AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<RayTracingFeatureProcessor>();
AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<DiffuseGlobalIlluminationFeatureProcessor>();
@@ -20,12 +20,17 @@ namespace AZ
{
namespace Render
{
class ModelReloaderSystem;
class CommonSystemComponent
: public AZ::Component
{
public:
AZ_COMPONENT(CommonSystemComponent, "{BFB8FE2B-C952-4D0C-8E32-4FE7C7A97757}");
CommonSystemComponent();
~CommonSystemComponent();
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
@@ -44,6 +49,8 @@ namespace AZ
RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler m_loadTemplatesHandler;
AZStd::unique_ptr<ModelReloaderSystem> m_modelReloaderSystem;
#if AZ_TRAIT_LUXCORE_SUPPORTED
// LuxCore
LuxCoreRenderer m_luxCore;
@@ -54,27 +54,10 @@ namespace AZ
m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass);
// retrieve the number of threads per thread group from the shader
const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" });
if (numThreads)
const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs);
if (!outcome.IsSuccess())
{
const RHI::ShaderStageAttributeArguments& args = *numThreads;
bool validArgs = args.size() == 3;
if (validArgs)
{
validArgs &= args[0].type() == azrtti_typeid<int>();
validArgs &= args[1].type() == azrtti_typeid<int>();
validArgs &= args[2].type() == azrtti_typeid<int>();
}
if (!validArgs)
{
AZ_Error("PassSystem", false, "[DiffuseProbeGridBlendDistancePass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str());
return;
}
m_dispatchArgs.m_threadsPerGroupX = static_cast<uint16_t>(AZStd::any_cast<int>(args[0]));
m_dispatchArgs.m_threadsPerGroupY = static_cast<uint16_t>(AZStd::any_cast<int>(args[1]));
m_dispatchArgs.m_threadsPerGroupZ = static_cast<uint16_t>(AZStd::any_cast<int>(args[2]));
AZ_Error("PassSystem", false, "[DiffuseProbeGridBlendDistancePass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str());
}
}
@@ -54,27 +54,10 @@ namespace AZ
m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass);
// retrieve the number of threads per thread group from the shader
const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" });
if (numThreads)
const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs);
if (!outcome.IsSuccess())
{
const RHI::ShaderStageAttributeArguments& args = *numThreads;
bool validArgs = args.size() == 3;
if (validArgs)
{
validArgs &= args[0].type() == azrtti_typeid<int>();
validArgs &= args[1].type() == azrtti_typeid<int>();
validArgs &= args[2].type() == azrtti_typeid<int>();
}
if (!validArgs)
{
AZ_Error("PassSystem", false, "[DiffuseProbeBlendIrradiancePass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str());
return;
}
m_dispatchArgs.m_threadsPerGroupX = static_cast<uint16_t>(AZStd::any_cast<int>(args[0]));
m_dispatchArgs.m_threadsPerGroupY = static_cast<uint16_t>(AZStd::any_cast<int>(args[1]));
m_dispatchArgs.m_threadsPerGroupZ = static_cast<uint16_t>(AZStd::any_cast<int>(args[2]));
AZ_Error("PassSystem", false, "[DiffuseProbeBlendIrradiancePass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str());
}
}
@@ -67,27 +67,10 @@ namespace AZ
srgLayout = shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass);
// retrieve the number of threads per thread group from the shader
const auto numThreads = shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" });
if (numThreads)
const auto outcome = RPI::GetComputeShaderNumThreads(shader->GetAsset(), dispatchArgs);
if (!outcome.IsSuccess())
{
const RHI::ShaderStageAttributeArguments& args = *numThreads;
bool validArgs = args.size() == 3;
if (validArgs)
{
validArgs &= args[0].type() == azrtti_typeid<int>();
validArgs &= args[1].type() == azrtti_typeid<int>();
validArgs &= args[2].type() == azrtti_typeid<int>();
}
if (!validArgs)
{
AZ_Error("PassSystem", false, "[DiffuseProbeGridBorderUpdatePass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str());
return;
}
dispatchArgs.m_threadsPerGroupX = static_cast<uint16_t>(AZStd::any_cast<int>(args[0]));
dispatchArgs.m_threadsPerGroupY = static_cast<uint16_t>(AZStd::any_cast<int>(args[1]));
dispatchArgs.m_threadsPerGroupZ = static_cast<uint16_t>(AZStd::any_cast<int>(args[2]));
AZ_Error("PassSystem", false, "[DiffuseProbeGridBorderUpdatePass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str());
}
}
@@ -58,27 +58,10 @@ namespace AZ
m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass);
// retrieve the number of threads per thread group from the shader
const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" });
if (numThreads)
const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs);
if (!outcome.IsSuccess())
{
const RHI::ShaderStageAttributeArguments& args = *numThreads;
bool validArgs = args.size() == 3;
if (validArgs)
{
validArgs &= args[0].type() == azrtti_typeid<int>();
validArgs &= args[1].type() == azrtti_typeid<int>();
validArgs &= args[2].type() == azrtti_typeid<int>();
}
if (!validArgs)
{
AZ_Error("PassSystem", false, "[DiffuseProbeClassificationPass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str());
return;
}
m_dispatchArgs.m_threadsPerGroupX = static_cast<uint16_t>(AZStd::any_cast<int>(args[0]));
m_dispatchArgs.m_threadsPerGroupY = static_cast<uint16_t>(AZStd::any_cast<int>(args[1]));
m_dispatchArgs.m_threadsPerGroupZ = static_cast<uint16_t>(AZStd::any_cast<int>(args[2]));
AZ_Error("PassSystem", false, "[DiffuseProbeClassificationPass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str());
}
}
@@ -58,27 +58,10 @@ namespace AZ
m_srgLayout = m_shader->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Pass);
// retrieve the number of threads per thread group from the shader
const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" });
if (numThreads)
const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), m_dispatchArgs);
if (!outcome.IsSuccess())
{
const RHI::ShaderStageAttributeArguments& args = *numThreads;
bool validArgs = args.size() == 3;
if (validArgs)
{
validArgs &= args[0].type() == azrtti_typeid<int>();
validArgs &= args[1].type() == azrtti_typeid<int>();
validArgs &= args[2].type() == azrtti_typeid<int>();
}
if (!validArgs)
{
AZ_Error("PassSystem", false, "[DiffuseProbeRelocationPass '%s']: Shader '%s' contains invalid numthreads arguments.", GetPathName().GetCStr(), shaderFilePath.c_str());
return;
}
m_dispatchArgs.m_threadsPerGroupX = static_cast<uint16_t>(AZStd::any_cast<int>(args[0]));
m_dispatchArgs.m_threadsPerGroupY = static_cast<uint16_t>(AZStd::any_cast<int>(args[1]));
m_dispatchArgs.m_threadsPerGroupZ = static_cast<uint16_t>(AZStd::any_cast<int>(args[2]));
AZ_Error("PassSystem", false, "[DiffuseProbeRelocationPass '%s']: Shader '%s' contains invalid numthreads arguments:\n%s", GetPathName().GetCStr(), shaderFilePath.c_str(), outcome.GetError().c_str());
}
}
@@ -10,6 +10,7 @@
#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
#include <Atom/Feature/RenderCommon.h>
#include <Atom/Feature/Mesh/MeshFeatureProcessor.h>
#include <Atom/Feature/Mesh/ModelReloaderSystemInterface.h>
#include <Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h>
#include <Atom/RPI.Public/Model/ModelLodUtils.h>
#include <Atom/RPI.Public/Scene.h>
@@ -18,6 +19,8 @@
#include <Atom/RPI.Reflect/Model/ModelAssetCreator.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AtomCore/Instance/InstanceDatabase.h>
#include <AzCore/Console/IConsole.h>
@@ -175,6 +178,7 @@ namespace AZ
{
if (meshHandle.IsValid())
{
meshHandle->m_meshLoader.reset();
meshHandle->DeInit();
m_transformService->ReleaseObjectId(meshHandle->m_objectId);
@@ -487,10 +491,12 @@ namespace AZ
}
Data::AssetBus::Handler::BusConnect(modelAsset.GetId());
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
}
MeshDataInstance::MeshLoader::~MeshLoader()
{
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
Data::AssetBus::Handler::BusDisconnect();
}
@@ -533,6 +539,7 @@ namespace AZ
if (model)
{
m_parent->RemoveRayTracingData();
m_parent->Init(model);
m_modelChangedEvent.Signal(AZStd::move(model));
}
@@ -545,10 +552,51 @@ namespace AZ
}
}
void MeshDataInstance::MeshLoader::OnModelReloaded(Data::Asset<Data::AssetData> asset)
{
OnAssetReady(asset);
}
void MeshDataInstance::MeshLoader::OnAssetError(Data::Asset<Data::AssetData> asset)
{
// Note: m_modelAsset and asset represents same asset, but only m_modelAsset contains the file path in its hint from serialization
AZ_Error("MeshDataInstance::MeshLoader", false, "Failed to load asset %s.", m_modelAsset.GetHint().c_str());
AZ_Error(
"MeshDataInstance::MeshLoader", false, "Failed to load asset %s. It may be missing, or not be finished processing",
m_modelAsset.GetHint().c_str());
AzFramework::AssetSystemRequestBus::Broadcast(
&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetByUuid, m_modelAsset.GetId().m_guid);
}
void MeshDataInstance::MeshLoader::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
if (assetId == m_modelAsset.GetId())
{
Data::Asset<RPI::ModelAsset> modelAssetReference = m_modelAsset;
// If the asset was modified, reload it
AZ::SystemTickBus::QueueFunction(
[=]() mutable
{
ModelReloaderSystemInterface::Get()->ReloadModel(modelAssetReference, m_modelReloadedEventHandler);
});
}
}
void MeshDataInstance::MeshLoader::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
{
if (assetId == m_modelAsset.GetId())
{
Data::Asset<RPI::ModelAsset> modelAssetReference = m_modelAsset;
// If the asset didn't exist in the catalog when it first attempted to load, we need to try loading it again
AZ::SystemTickBus::QueueFunction(
[=]() mutable
{
ModelReloaderSystemInterface::Get()->ReloadModel(modelAssetReference, m_modelReloadedEventHandler);
});
}
}
// MeshDataInstance...
@@ -557,14 +605,8 @@ namespace AZ
{
m_scene->GetCullingScene()->UnregisterCullable(m_cullable);
// remove from ray tracing
RayTracingFeatureProcessor* rayTracingFeatureProcessor = m_scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
if (rayTracingFeatureProcessor)
{
rayTracingFeatureProcessor->RemoveMesh(m_objectId);
}
RemoveRayTracingData();
m_meshLoader.reset();
m_drawPacketListsByLod.clear();
m_materialAssignments.clear();
m_shaderResourceGroup = {};
@@ -951,6 +993,16 @@ namespace AZ
rayTracingFeatureProcessor->SetMesh(m_objectId, m_model->GetModelAsset()->GetId(), subMeshes);
}
void MeshDataInstance::RemoveRayTracingData()
{
// remove from ray tracing
RayTracingFeatureProcessor* rayTracingFeatureProcessor = m_scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
if (rayTracingFeatureProcessor)
{
rayTracingFeatureProcessor->RemoveMesh(m_objectId);
}
}
void MeshDataInstance::SetSortKey(RHI::DrawItemSortKey sortKey)
{
m_sortKey = sortKey;
@@ -0,0 +1,175 @@
/*
* 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 <Source/Mesh/ModelReloader.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Public/Model/Model.h>
namespace AZ
{
namespace Render
{
ModelReloader::ModelReloader(
Data::Asset<RPI::ModelAsset> modelAsset, RemoveModelFromReloaderSystemEvent::Handler& removeReloaderFromSystemHandler)
{
m_modelAsset.push_back(modelAsset);
m_pendingDependencyListStatus.reset();
removeReloaderFromSystemHandler.Connect(m_onRemoveReloaderFromSystem);
// Iterate over the model and track the assets that need to be reloaded
for (auto& modelLodAsset : modelAsset->GetLodAssets())
{
for (auto& mesh : modelLodAsset->GetMeshes())
{
for (auto& streamBufferInfo : mesh.GetStreamBufferInfoList())
{
InsertMeshDependencyIfUnique(streamBufferInfo.m_bufferAssetView.GetBufferAsset());
}
InsertMeshDependencyIfUnique(mesh.GetIndexBufferAssetView().GetBufferAsset());
}
m_modelDependencies.push_back(modelLodAsset);
}
AZ_Assert(
m_meshDependencies.size() <= m_pendingDependencyListStatus.size(),
"There are more buffers used by the model %s than are supported by the ModelReloader.", modelAsset.GetHint().c_str());
m_state = State::WaitingForMeshDependencies;
ReloadDependenciesAndWait();
}
void ModelReloader::ConnectOnReloadedEventHandler(ModelReloadedEvent::Handler& onReloadedEventHandler)
{
onReloadedEventHandler.Connect(m_onModelReloaded);
}
void ModelReloader::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
DependencyList& pendingDependencies = GetPendingDependencyList();
const Data::AssetId& reloadedAssetId = asset.GetId();
// Find the index of the asset that was reloaded
const auto matchesId = [reloadedAssetId](const Data::Asset<Data::AssetData>& asset){ return asset.GetId() == reloadedAssetId;};
const auto& iter = AZStd::find_if(AZStd::begin(pendingDependencies), AZStd::end(pendingDependencies), matchesId);
AZ_Assert(
iter != AZStd::end(pendingDependencies),
"ModelReloader - handling an AssetReloaded event for an asset that is not part of the dependency list.");
size_t currentIndex = AZStd::distance(AZStd::begin(pendingDependencies), iter);
// Keep a reference to the newly reloaded asset to prevent it from being immediately released
pendingDependencies[currentIndex] = asset;
Data::AssetBus::MultiHandler::BusDisconnect(reloadedAssetId);
// Clear the bit, now that it has been reloaded
m_pendingDependencyListStatus.reset(currentIndex);
if (m_pendingDependencyListStatus.none())
{
AdvanceToNextLevelOfHierarchy();
}
}
void ModelReloader::OnAssetReloadError(Data::Asset<Data::AssetData> asset)
{
// An error is actually okay/expected in some situations.
// For example, if the 2nd UV set was removed, and we tried to reload the second uv set, the reload would fail.
// We want to treat it as a success, and mark that dependency as 'up to date'
OnAssetReloaded(asset);
}
void ModelReloader::InsertMeshDependencyIfUnique(Data::Asset<Data::AssetData> asset)
{
if (AZStd::find(AZStd::begin(m_meshDependencies), AZStd::end(m_meshDependencies), asset) == AZStd::end(m_meshDependencies))
{
// Multiple meshes may reference the same buffer, so only add the dependency if it is unique
m_meshDependencies.push_back(asset);
}
}
void ModelReloader::ReloadDependenciesAndWait()
{
// Get the current list of dependencies depending on the current state
DependencyList& dependencies = GetPendingDependencyList();
if (!m_pendingDependencyListStatus.none())
{
AZ_Assert(
m_pendingDependencyListStatus.none(),
"ModelReloader attempting to add new dependencies while still waiting for other dependencies in the hierarchy to "
"load.");
}
if (dependencies.empty())
{
// If the original model asset failed to load, it won't have any dependencies to reload
AdvanceToNextLevelOfHierarchy();
}
AZ_Assert(
dependencies.size() <= m_pendingDependencyListStatus.size(),
"ModelReloader has more dependencies than can fit in the bitset. The size of m_pendingDependencyListStatus needs to be increased.");
// Set all bits to 1
m_pendingDependencyListStatus.set();
// Clear the least significant n-bits
m_pendingDependencyListStatus <<= dependencies.size();
// Set the least significant n-bits to 1, and the rest to 0
m_pendingDependencyListStatus.flip();
// Reload all the assets
for (Data::Asset<Data::AssetData>& dependencyAsset : dependencies)
{
Data::AssetBus::MultiHandler::BusConnect(dependencyAsset.GetId());
dependencyAsset.Reload();
}
}
void ModelReloader::AdvanceToNextLevelOfHierarchy()
{
switch (m_state)
{
case State::WaitingForMeshDependencies:
m_state = State::WaitingForModelDependencies;
ReloadDependenciesAndWait();
break;
case State::WaitingForModelDependencies:
m_state = State::WaitingForModel;
ReloadDependenciesAndWait();
break;
case State::WaitingForModel:
Data::AssetBus::MultiHandler::BusDisconnect();
// Since the model asset is finished reloading, orphan model from the instance database
// so that all of the buffer instances are re-created with the latest data
RPI::Model::TEMPOrphanFromDatabase(m_modelAsset.front());
// Signal that the model is ready
m_onModelReloaded.Signal(m_modelAsset.front());
// Remove this reloader from the ModelReloaderSystem
m_onRemoveReloaderFromSystem.Signal(m_modelAsset.front().GetId());
delete this;
break;
}
}
ModelReloader::DependencyList& ModelReloader::GetPendingDependencyList()
{
switch (m_state)
{
case State::WaitingForMeshDependencies:
return m_meshDependencies;
break;
case State::WaitingForModelDependencies:
return m_modelDependencies;
break;
case State::WaitingForModel:
default:
return m_modelAsset;
break;
}
}
} // namespace Render
} // namespace AZ
@@ -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 <AzCore/Asset/AssetCommon.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <Atom/Feature/Mesh/ModelReloaderSystemInterface.h>
#include <Source/Mesh/ModelReloaderSystem.h>
namespace AZ
{
namespace RPI
{
class ModelAsset;
}
namespace Render
{
//! ModelReloader takes care of reloading Buffer, ModelLod, and Model assets in the correct order
//! The ModelReloaderSystem should be used to reload a model, rather than using a ModelReloader directly
class ModelReloader
: private Data::AssetBus::MultiHandler
{
using DependencyList = AZStd::vector<Data::Asset<Data::AssetData>>;
public:
AZ_RTTI(AZ::Render::ModelReloader, "{99B75A6A-62B6-490A-9953-029BE7D69452}");
ModelReloader() = default;
//! Reload a model asset
//! @param modelAsset - the asset to be reloaded
//! @param removeReloaderFromSystemHandler - an event that will tell the ModelReloaderSystem when to remove the reloader because it is finished
ModelReloader(Data::Asset<RPI::ModelAsset> modelAsset, RemoveModelFromReloaderSystemEvent::Handler& removeReloaderFromSystemHandler);
//! Connects a handler that will handle an event when the model is finished reloading
void ConnectOnReloadedEventHandler(ModelReloadedEvent::Handler& onReloadedEventHandler);
private:
enum class State
{
WaitingForMeshDependencies,
WaitingForModelDependencies,
WaitingForModel
};
// Data::AssetBus::MultiHandler overrides...
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReloadError(Data::Asset<Data::AssetData> asset) override;
void InsertMeshDependencyIfUnique(Data::Asset<Data::AssetData> asset);
void ReloadDependenciesAndWait();
void AdvanceToNextLevelOfHierarchy();
DependencyList& GetPendingDependencyList();
ModelReloadedEvent m_onModelReloaded;
RemoveModelFromReloaderSystemEvent m_onRemoveReloaderFromSystem;
// Keep track of all the asset references for each level of the hierarchy
DependencyList m_modelAsset;
DependencyList m_meshDependencies;
DependencyList m_modelDependencies;
AZStd::bitset<1024> m_pendingDependencyListStatus;
State m_state;
};
} // namespace Render
} // namespace AZ
@@ -0,0 +1,38 @@
/*
* 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 <Source/Mesh/ModelReloaderSystem.h>
#include <Source/Mesh/ModelReloader.h>
#include <AzCore/std/parallel/scoped_lock.h>
namespace AZ
{
namespace Render
{
void ModelReloaderSystem::ReloadModel(Data::Asset<RPI::ModelAsset> modelAsset, ModelReloadedEvent::Handler& onReloadedEventHandler)
{
AZStd::scoped_lock lock(m_pendingReloadMutex);
if (m_pendingReloads.find(modelAsset.GetId()) == m_pendingReloads.end())
{
ModelReloader* reloader = new ModelReloader(modelAsset, m_removeModelHandler);
m_pendingReloads[modelAsset.GetId()] = reloader;
}
m_pendingReloads[modelAsset.GetId()]->ConnectOnReloadedEventHandler(onReloadedEventHandler);
}
void ModelReloaderSystem::RemoveReloader(const Data::AssetId& assetId)
{
AZStd::scoped_lock lock(m_pendingReloadMutex);
// We don't delete the ModelReloader here, because its in the middle of signaling this RemoveReloader event.
// We only remove it from the pending reloads here.
// The ModelReloader will delete itself after it finishes firing this event.
m_pendingReloads.erase(assetId);
}
} // namespace Render
} // namespace AZ
@@ -0,0 +1,50 @@
/*
* 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 <Atom/Feature/Mesh/ModelReloaderSystemInterface.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/unordered_map.h>
namespace AZ
{
namespace Render
{
class ModelReloader;
using RemoveModelFromReloaderSystemEvent = Event<const Data::AssetId&>;
class ModelReloaderSystem
: public ModelReloaderSystemInterface
{
public:
AZ_RTTI(Render::ModelReloaderSystem, "{8C85ECCD-B6C8-4949-B26C-9C4F1020F2B8}", Render::ModelReloaderSystemInterface);
void ReloadModel(Data::Asset<RPI::ModelAsset> modelAsset, ModelReloadedEvent::Handler& onReloadedEventHandler) override;
private:
void RemoveReloader(const Data::AssetId& assetId);
// Keep track of all the pending reloads so there are no duplicates
AZStd::unordered_map<Data::AssetId, ModelReloader*> m_pendingReloads;
AZStd::mutex m_pendingReloadMutex;
RemoveModelFromReloaderSystemEvent::Handler m_removeModelHandler{
[&](const Data::AssetId& assetId)
{
RemoveReloader(assetId);
} };
friend class ModelReloader;
};
} // namespace Render
} // namespace AZ
@@ -13,6 +13,7 @@
#include <Atom/RPI.Public/Shader/Shader.h>
#include <Atom/RPI.Public/Model/ModelLod.h>
#include <Atom/RPI.Public/Buffer/Buffer.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/BufferView.h>
@@ -79,15 +80,11 @@ namespace AZ
m_dispatchItem.m_pipelineState = m_morphTargetShader->AcquirePipelineState(pipelineStateDescriptor);
// Get the threads-per-group values from the compute shader [numthreads(x,y,z)]
const auto& numThreads = m_morphTargetShader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, AZ::Name{ "numthreads" });
auto& arguments = m_dispatchItem.m_arguments.m_direct;
if (numThreads)
const auto outcome = RPI::GetComputeShaderNumThreads(m_morphTargetShader->GetAsset(), arguments);
if (!outcome.IsSuccess())
{
const auto& args = *numThreads;
// Check that the arguments are valid integers, and fall back to 1,1,1 if there is an error
arguments.m_threadsPerGroupX = static_cast<uint16_t>(args[0].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[0]) : 1);
arguments.m_threadsPerGroupY = static_cast<uint16_t>(args[1].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[1]) : 1);
arguments.m_threadsPerGroupZ = static_cast<uint16_t>(args[2].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[2]) : 1);
AZ_Error("MorphTargetDispatchItem", false, outcome.GetError().c_str());
}
arguments.m_totalNumberOfThreadsX = m_morphTargetMetaData.m_vertexCount;
@@ -14,6 +14,7 @@
#include <Atom/RPI.Public/Shader/Shader.h>
#include <Atom/RPI.Public/Model/ModelLod.h>
#include <Atom/RPI.Public/Buffer/Buffer.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/BufferView.h>
@@ -199,17 +200,14 @@ namespace AZ
m_instanceSrg->Compile();
m_dispatchItem.m_uniqueShaderResourceGroup = m_instanceSrg->GetRHIShaderResourceGroup();
m_dispatchItem.m_pipelineState = m_skinningShader->AcquirePipelineState(pipelineStateDescriptor);
const auto& numThreads = m_skinningShader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, AZ::Name{ "numthreads" });
auto& arguments = m_dispatchItem.m_arguments.m_direct;
if (numThreads)
{
const auto& args = *numThreads;
arguments.m_threadsPerGroupX = static_cast<uint16_t>(args[0].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[0]) : 1);
arguments.m_threadsPerGroupY = static_cast<uint16_t>(args[1].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[1]) : 1);
arguments.m_threadsPerGroupZ = static_cast<uint16_t>(args[2].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[2]) : 1);
}
auto& arguments = m_dispatchItem.m_arguments.m_direct;
const auto outcome = RPI::GetComputeShaderNumThreads(m_skinningShader->GetAsset(), arguments);
if (!outcome.IsSuccess())
{
AZ_Error("SkinnedMeshInputBuffers", false, outcome.GetError().c_str());
}
arguments.m_totalNumberOfThreadsX = xThreads;
arguments.m_totalNumberOfThreadsY = yThreads;
arguments.m_totalNumberOfThreadsZ = 1;
@@ -26,6 +26,7 @@ set(FILES
Include/Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessor.h
Include/Atom/Feature/LookupTable/LookupTableAsset.h
Include/Atom/Feature/Mesh/MeshFeatureProcessor.h
Include/Atom/Feature/Mesh/ModelReloaderSystemInterface.h
Include/Atom/Feature/PostProcessing/PostProcessingConstants.h
Include/Atom/Feature/PostProcessing/SMAAFeatureProcessorInterface.h
Include/Atom/Feature/PostProcess/PostFxLayerCategoriesConstants.h
@@ -169,6 +170,10 @@ set(FILES
Source/Math/MathFilter.cpp
Source/Math/MathFilterDescriptor.h
Source/Mesh/MeshFeatureProcessor.cpp
Source/Mesh/ModelReloader.cpp
Source/Mesh/ModelReloader.h
Source/Mesh/ModelReloaderSystem.cpp
Source/Mesh/ModelReloaderSystem.h
Source/MorphTargets/MorphTargetComputePass.cpp
Source/MorphTargets/MorphTargetComputePass.h
Source/MorphTargets/MorphTargetDispatchItem.cpp
@@ -166,6 +166,10 @@ namespace AZ
AZStd::array_view<ConstPtr<ImageView>> GetImageGroup() const;
AZStd::array_view<ConstPtr<BufferView>> GetBufferGroup() const;
AZStd::array_view<SamplerState> GetSamplerGroup() const;
//! Reset image and buffer views setup for this ShaderResourceGroupData
//! So it won't hold references for any RHI resources
void ResetViews();
//! Returns the opaque constant data populated by calls to SetConstant and SetConstantData.
//!
@@ -330,6 +330,14 @@ namespace AZ
return m_samplers;
}
void ShaderResourceGroupData::ResetViews()
{
m_imageViews.assign(m_imageViews.size(), nullptr);
m_bufferViews.assign(m_bufferViews.size(), nullptr);
m_imageViewsUnboundedArray.assign(m_imageViewsUnboundedArray.size(), nullptr);
m_bufferViewsUnboundedArray.assign(m_bufferViewsUnboundedArray.size(), nullptr);
}
AZStd::array_view<uint8_t> ShaderResourceGroupData::GetConstantData() const
{
return m_constantsData.GetConstantData();
+1 -1
View File
@@ -28,7 +28,7 @@ namespace UnitTest
m_application.reset();
}
static constexpr const char TestDataFolder[] = "@devroot@/Gems/Atom/RHI/Code/Tests/UtilsTestsData/";
static constexpr const char TestDataFolder[] = "@engroot@/Gems/Atom/RHI/Code/Tests/UtilsTestsData/";
AZStd::unique_ptr<AzFramework::Application> m_application;
};
@@ -44,14 +44,14 @@ namespace AZ
AZStd::string GetSourcePathByAssetId(const AZ::Data::AssetId& assetId);
//! Tries to resolve a relative file reference, given the path of a referencing file.
//! @param originatingSourceFilePath Path to the parent file that references referencedSourceFilePath. May be absolute or relative to asset-root.
//! @param originatingSourceFilePath Path to the parent file that references referencedSourceFilePath. May be absolute or relative to asset-root.
//! @param referencedSourceFilePath Path that the parent file references. May be relative to the parent file location or relative to asset-root.
//! @return A full path for referencedSourceFilePath, if a full path was found. If a full path could not be constructed, returns referencedSourceFilePath unmodified.
AZStd::string ResolvePathReference(const AZStd::string& originatingSourceFilePath, const AZStd::string& referencedSourceFilePath);
//! Returns the list of paths where a source asset file could possibly appear.
//! Returns the list of paths where a source asset file could possibly appear.
//! This is intended for use by AssetBuilders when reporting dependencies, to support relative paths between source files.
//! When a source data file references another file using a relative path, the path might be relative to the originating
//! When a source data file references another file using a relative path, the path might be relative to the originating
//! file or it might be a standard source asset path (i.e. relative to the logical asset-root). This function will help reporting
//! dependencies on all possible locations where that file may appear at some point in the future.
//! For example a file MyGem/Assets/Foo/a.json might reference another file as "Bar/b.json". In this case, calling
@@ -94,9 +94,9 @@ namespace AZ
template<typename AssetDataT>
Outcome<AZ::Data::Asset<AssetDataT>> LoadAsset(const AZ::Data::AssetId& assetId, [[maybe_unused]] const char* sourcePathForDebug)
{
if (nullptr == AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@"))
if (nullptr == AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@"))
{
// The absence of "@assets@" is not necessarily the reason LoadAsset() can't be used in CreateJobs(), but it
// The absence of "@products@" is not necessarily the reason LoadAsset() can't be used in CreateJobs(), but it
// is a symptom of calling LoadAsset() from CreateJobs() which is not supported.
AZ_Assert(false, "It appears AssetUtils::LoadAsset() is being called in CreateJobs(). It can only be used in ProcessJob().");
return AZ::Failure();
@@ -268,6 +268,8 @@ namespace AZ
AZStd::vector<RHI::StreamBufferView> m_cachedStreamBufferViews;
AZStd::vector<RHI::IndexBufferView> m_cachedIndexBufferViews;
AZStd::vector<Data::Instance<ShaderResourceGroup>> m_cachedDrawSrg;
uint32_t m_nextDrawSrgIdx = 0;
// structure includes DrawItem and stream and index buffer index
using BufferViewIndexType = uint32_t;
@@ -37,6 +37,10 @@ namespace AZ
static Data::Instance<Model> FindOrCreate(const Data::Asset<ModelAsset>& modelAsset);
//! Orphan the model, its lods, and all their buffers so that they can be replaced in the instance database
//! This is a temporary function, that will be removed once the Model/ModelAsset classes no longer need it
static void TEMPOrphanFromDatabase(const Data::Asset<ModelAsset>& modelAsset);
~Model() = default;
//! Blocks the CPU until the streaming upload is complete. Returns immediately if no
@@ -11,6 +11,7 @@
#include <AtomCore/Instance/Instance.h>
#include <Atom/RHI/DispatchItem.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Image/StreamingImage.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
@@ -40,6 +41,23 @@ namespace AZ
//! Loads a streaming image asset for the given file path
Data::Instance<RPI::StreamingImage> LoadStreamingTexture(AZStd::string_view path);
//! Looks for a three arguments attribute named @attributeName in the given shader asset.
//! Assigns the value to each non-null output variables.
//! @param shaderAsset
//! @param attributeName
//! @param numThreadsX Can be NULL. If not NULL it takes the value of the 1st argument of the attribute. Becomes 1 on error.
//! @param numThreadsY Can be NULL. If not NULL it takes the value of the 2nd argument of the attribute. Becomes 1 on error.
//! @param numThreadsZ Can be NULL. If not NULL it takes the value of the 3rd argument of the attribute. Becomes 1 on error.
//! @returns An Outcome instance with error message in case of error.
AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& attributeName, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ);
//! Same as above, but assumes the name of the attribute to be 'numthreads'.
AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(const Data::Asset<ShaderAsset>& shaderAsset, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ);
//! Same as above. Provided as a convenience when all arguments of the 'numthreads' attributes should be assigned to RHI::DispatchDirect::m_threadsPerGroup* variables.
AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(const Data::Asset<ShaderAsset>& shaderAsset, RHI::DispatchDirect& dispatchDirect);
} // namespace RPI
} // namespace AZ
@@ -133,6 +133,9 @@ namespace AZ
/// Returns an array of RPI buffers associated with the buffer shader input index.
AZStd::array_view<Data::Instance<Buffer>> GetBufferArray(RHI::ShaderInputNameIndex& inputIndex) const;
AZStd::array_view<Data::Instance<Buffer>> GetBufferArray(RHI::ShaderInputBufferIndex inputIndex) const;
//! Reset image and buffer views so that it won't hold references for any RHI resources
void ResetViews();
//////////////////////////////////////////////////////////////////////////
// Methods for assignment / access of RHI Image types.
@@ -60,6 +60,12 @@ namespace AZ
const AZStd::string& GetName() const;
private:
// AssetData overrides...
bool HandleAutoReload() override
{
return false;
}
// Called by asset creators to assign the asset to a ready state.
void SetReady();
@@ -77,6 +77,12 @@ namespace AZ
float& distanceNormalized, AZ::Vector3& normal) const;
private:
// AssetData overrides...
bool HandleAutoReload() override
{
return false;
}
void SetReady();
AZ::Name m_name;
@@ -149,6 +149,12 @@ namespace AZ
const AZ::Aabb& GetAabb() const;
private:
// AssetData overrides...
bool HandleAutoReload() override
{
return false;
}
AZStd::vector<Mesh> m_meshes;
AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
@@ -518,7 +518,6 @@ namespace AZ
if (drawSrg)
{
drawItem.m_uniqueShaderResourceGroup = drawSrg->GetRHIShaderResourceGroup();
m_cachedDrawSrg.push_back(drawSrg);
}
// Set scissor per draw if scissor is enabled.
@@ -608,7 +607,6 @@ namespace AZ
if (drawSrg)
{
drawItem.m_uniqueShaderResourceGroup = drawSrg->GetRHIShaderResourceGroup();
m_cachedDrawSrg.push_back(drawSrg);
}
// Set scissor per draw if scissor is enabled.
@@ -635,7 +633,22 @@ namespace AZ
{
return nullptr;
}
auto drawSrg = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), m_shader->GetSupervariantIndex(), m_drawSrgLayout->GetName());
Data::Instance<ShaderResourceGroup> drawSrg;
if (m_nextDrawSrgIdx == m_cachedDrawSrg.size())
{
drawSrg = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), m_shader->GetSupervariantIndex(), m_drawSrgLayout->GetName());
m_cachedDrawSrg.push_back(drawSrg);
}
else if (m_nextDrawSrgIdx < m_cachedDrawSrg.size())
{
drawSrg = m_cachedDrawSrg[m_nextDrawSrgIdx];
}
else
{
AZ_Assert(false, "Unexpected next draw srg index");
}
m_nextDrawSrgIdx++;
// Set fallback value for shader variant if draw srg contains constant for shader variant fallback
if (m_hasShaderVariantKeyFallbackEntry)
@@ -727,7 +740,7 @@ namespace AZ
}
for (auto& drawItemProperties : m_cachedDrawList)
{
{
view->AddDrawItem(m_drawListTag, drawItemProperties);
}
}
@@ -743,9 +756,14 @@ namespace AZ
m_cachedDrawItems.clear();
m_cachedStreamBufferViews.clear();
m_cachedIndexBufferViews.clear();
m_cachedDrawSrg.clear();
m_cachedDrawList.clear();
m_nextDrawSrgIdx = 0;
m_drawFinalized = false;
for (auto srg:m_cachedDrawSrg)
{
srg->ResetViews();
}
}
const RHI::PipelineState* DynamicDrawContext::GetCurrentPipelineState()
@@ -31,6 +31,28 @@ namespace AZ
modelAsset);
}
void Model::TEMPOrphanFromDatabase(const Data::Asset<ModelAsset>& modelAsset)
{
for (auto& modelLodAsset : modelAsset->GetLodAssets())
{
for(auto& mesh : modelLodAsset->GetMeshes())
{
for (auto& streamBufferInfo : mesh.GetStreamBufferInfoList())
{
Data::InstanceDatabase<Buffer>::Instance().TEMPOrphan(
Data::InstanceId::CreateFromAssetId(streamBufferInfo.m_bufferAssetView.GetBufferAsset().GetId()));
}
Data::InstanceDatabase<Buffer>::Instance().TEMPOrphan(
Data::InstanceId::CreateFromAssetId(mesh.GetIndexBufferAssetView().GetBufferAsset().GetId()));
}
Data::InstanceDatabase<ModelLod>::Instance().TEMPOrphan(Data::InstanceId::CreateFromAssetId(modelLodAsset.GetId()));
}
Data::InstanceDatabase<Model>::Instance().TEMPOrphan(
Data::InstanceId::CreateFromAssetId(modelAsset.GetId()));
}
size_t Model::GetLodCount() const
{
return m_lods.size();
@@ -107,30 +107,13 @@ namespace AZ
dispatchArgs.m_totalNumberOfThreadsY = passData->m_totalNumberOfThreadsY;
dispatchArgs.m_totalNumberOfThreadsZ = passData->m_totalNumberOfThreadsZ;
const auto numThreads = m_shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name{ "numthreads" });
if (numThreads)
const auto outcome = RPI::GetComputeShaderNumThreads(m_shader->GetAsset(), dispatchArgs);
if (!outcome.IsSuccess())
{
const RHI::ShaderStageAttributeArguments& args = *numThreads;
bool validArgs = args.size() == 3;
if (validArgs)
{
validArgs &= args[0].type() == azrtti_typeid<int>();
validArgs &= args[1].type() == azrtti_typeid<int>();
validArgs &= args[2].type() == azrtti_typeid<int>();
}
if (!validArgs)
{
AZ_Error("PassSystem", false, "[ComputePass '%s']: Shader '%s' contains invalid numthreads arguments.",
GetPathName().GetCStr(),
passData->m_shaderReference.m_filePath.data());
return;
}
dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(AZStd::any_cast<int>(args[0]));
dispatchArgs.m_threadsPerGroupY = aznumeric_cast<uint16_t>(AZStd::any_cast<int>(args[1]));
dispatchArgs.m_threadsPerGroupZ = aznumeric_cast<uint16_t>(AZStd::any_cast<int>(args[2]));
AZ_Error("PassSystem", false, "[ComputePass '%s']: Shader '%.*s' contains invalid numthreads arguments:\n%s",
GetPathName().GetCStr(), passData->m_shaderReference.m_filePath.size(), passData->m_shaderReference.m_filePath.data(), outcome.GetError().c_str());
}
m_dispatchItem.m_arguments = dispatchArgs;
m_isFullscreenPass = passData->m_makeFullscreenPass;
@@ -143,5 +143,79 @@ namespace AZ
return RPI::StreamingImage::FindOrCreate(streamingImageAsset);
}
//! A helper function for GetComputeShaderNumThreads(), to consolidate error messages, etc.
static bool GetAttributeArgumentByIndex(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& attributeName, const RHI::ShaderStageAttributeArguments& args, const size_t argIndex, uint16_t* value, AZStd::string& errorMsg)
{
if (value)
{
const auto numArguments = args.size();
if (numArguments > argIndex)
{
if (args[argIndex].type() == azrtti_typeid<int>())
{
*value = aznumeric_caster(AZStd::any_cast<int>(args[argIndex]));
}
else
{
errorMsg = AZStd::string::format("Was expecting argument '%zu' in attribute '%s' to be of type 'int' from shader asset '%s'", argIndex, attributeName.GetCStr(), shaderAsset.GetHint().c_str());
return false;
}
}
else
{
errorMsg = AZStd::string::format("Was expecting at least '%zu' arguments in attribute '%s' from shader asset '%s'", argIndex + 1, attributeName.GetCStr(), shaderAsset.GetHint().c_str());
return false;
}
}
return true;
}
AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(const Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& attributeName, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ)
{
// Set default 1, 1, 1 now. In case of errors later this is what the caller will get.
if (numThreadsX)
{
*numThreadsX = 1;
}
if (numThreadsY)
{
*numThreadsY = 1;
}
if (numThreadsZ)
{
*numThreadsZ = 1;
}
const auto numThreads = shaderAsset->GetAttribute(RHI::ShaderStage::Compute, attributeName);
if (!numThreads)
{
return AZ::Failure(AZStd::string::format("Couldn't find attribute '%s' in shader asset '%s'", attributeName.GetCStr(), shaderAsset.GetHint().c_str()));
}
const RHI::ShaderStageAttributeArguments& args = *numThreads;
AZStd::string errorMsg;
if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 0, numThreadsX, errorMsg))
{
return AZ::Failure(errorMsg);
}
if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 1, numThreadsY, errorMsg))
{
return AZ::Failure(errorMsg);
}
if (!GetAttributeArgumentByIndex(shaderAsset, attributeName, args, 2, numThreadsZ, errorMsg))
{
return AZ::Failure(errorMsg);
}
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(const Data::Asset<ShaderAsset>& shaderAsset, uint16_t* numThreadsX, uint16_t* numThreadsY, uint16_t* numThreadsZ)
{
return GetComputeShaderNumThreads(shaderAsset, Name{ "numthreads" }, numThreadsX, numThreadsY, numThreadsZ);
}
AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(const Data::Asset<ShaderAsset>& shaderAsset, RHI::DispatchDirect& dispatchDirect)
{
return GetComputeShaderNumThreads(shaderAsset, &dispatchDirect.m_threadsPerGroupX, &dispatchDirect.m_threadsPerGroupY, &dispatchDirect.m_threadsPerGroupZ);
}
}
}
@@ -580,6 +580,11 @@ namespace AZ
return {};
}
void ShaderResourceGroup::ResetViews()
{
m_data.ResetViews();
}
const RHI::SamplerState& ShaderResourceGroup::GetSampler(RHI::ShaderInputNameIndex& inputIndex, uint32_t arrayIndex) const
{
inputIndex.ValidateOrFindSamplerIndex(GetLayout());
@@ -53,16 +53,6 @@ namespace UnitTest
return false;
}
const char* AssetSystemStub::GetAbsoluteDevGameFolderPath()
{
return nullptr;
}
const char* AssetSystemStub::GetAbsoluteDevRootFolderPath()
{
return nullptr;
}
bool AssetSystemStub::GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath)
{
return false;
@@ -56,8 +56,6 @@ namespace UnitTest
AZStd::unordered_map<AZStd::string, SourceInfo> m_sourceInfoMap;
bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override;
const char* GetAbsoluteDevGameFolderPath() override;
const char* GetAbsoluteDevRootFolderPath() override;
bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override;
bool GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder) override;
@@ -67,7 +67,7 @@ namespace UnitTest
AZ::IO::Path assetPath = AZStd::string_view{ AZ::Utils::GetProjectPath() };
assetPath /= "Cache";
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", assetPath.c_str());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetPath.c_str());
m_jsonRegistrationContext = AZStd::make_unique<AZ::JsonRegistrationContext>();
m_jsonSystemComponent = AZStd::make_unique<AZ::JsonSystemComponent>();
@@ -90,7 +90,7 @@ namespace UnitTest
JobManagerThreadDesc threadDesc;
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
threadDesc.m_cpuId = 0; // Don't set processors IDs on windows
#endif
#endif
uint32_t numWorkerThreads = AZStd::thread::hardware_concurrency();
@@ -99,7 +99,7 @@ namespace UnitTest
desc.m_workerThreads.push_back(threadDesc);
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
threadDesc.m_cpuId++;
#endif
#endif
}
m_jobManager = AZStd::make_unique<JobManager>(desc);
@@ -0,0 +1,18 @@
{
"description": "",
"materialType": "Materials/Types/StandardMultilayerPBR.materialtype",
"parentMaterial": "",
"propertyLayoutVersion": 3,
"properties": {
"blend": {
"blendSource": "BlendMaskVertexColors",
"debugDrawMode": "FinalBlendWeights",
"enableLayer2": true,
"enableLayer3": true,
"textureMap": ""
},
"parallax": {
"enable": false
}
}
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b6d860c6fc3703914716a97910899eca2e66927688690646f697c6246ac310fb
size 317340
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:91b8153f93e4773d872478fef40ff00c1b8d5f1ef13101cf62f81956e1c30107
size 68800
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2cf646a0a977c2edc5ee2ab6351ef633f194ce60c59ea3cb8359f71648db668e
size 374492
@@ -173,7 +173,7 @@ namespace AtomToolsFramework
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
&AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@assets@/assetcatalog.xml");
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml");
if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized())
{
@@ -289,7 +289,7 @@ namespace AtomToolsFramework
ExitMainLoop();
}
}
void AtomToolsApplication::SaveSettings()
{
if (m_activatedLocalUserSettings)
@@ -378,7 +378,7 @@ namespace AtomToolsFramework
ExitMainLoop();
}
}
bool AtomToolsApplication::LaunchLocalServer()
{
// Determine if this is the first launch of the tool by attempting to connect to a running server
@@ -24,7 +24,7 @@
namespace MaterialEditor
{
CreateMaterialDialog::CreateMaterialDialog(QWidget* parent)
: CreateMaterialDialog(QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) + AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials", parent)
: CreateMaterialDialog(QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) + AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials", parent)
{
}
@@ -106,7 +106,7 @@ namespace MaterialEditor
menu->addAction("Create Material...", [entry]()
{
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
@@ -182,7 +182,7 @@ namespace MaterialEditor
menu->addAction("Create Child Material...", [entry]()
{
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
@@ -346,7 +346,7 @@ namespace MaterialEditor
AZStd::string ViewportSettingsInspector::GetDefaultUniqueSaveFilePath(const AZStd::string& baseName) const
{
AZStd::string savePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
AZStd::string savePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@");
savePath += AZ_CORRECT_FILESYSTEM_SEPARATOR;
savePath += "Materials";
savePath += AZ_CORRECT_FILESYSTEM_SEPARATOR;
@@ -16,10 +16,10 @@ import sys
import os.path
import filecmp
g_devroot = azlmbr.paths.devroot
sys.path.append(os.path.join(g_devroot, 'Tests', 'Atom', 'Automated'))
g_engroot = azlmbr.paths.engroot
sys.path.append(os.path.join(g_engroot, 'Tests', 'Atom', 'Automated'))
g_materialTestFolder = os.path.join(g_devroot,'Gems','Atom','TestData','TestData','Materials','StandardPbrTestCases')
g_materialTestFolder = os.path.join(g_engroot,'Gems','Atom','TestData','TestData','Materials','StandardPbrTestCases')
# Change this to True to replace the expected screenshot images
g_replaceExpectedScreenshots = False
@@ -135,7 +135,7 @@ def main():
# clean previously generated shader variant list file so they don't clash.
pre, ext = os.path.splitext(shaderAssetInfo.relativePath)
projectShaderVariantListFilePath = os.path.join(azlmbr.paths.devassets, PROJECT_SHADER_VARIANTS_FOLDER, f'{pre}.shadervariantlist')
projectShaderVariantListFilePath = os.path.join(azlmbr.paths.projectroot, PROJECT_SHADER_VARIANTS_FOLDER, f'{pre}.shadervariantlist')
pre, ext = os.path.splitext(filename)
defaultShaderVariantListFilePath = f'{pre}.shadervariantlist'
@@ -46,7 +46,7 @@ static void DumfontTexture(IConsoleCmdArgs* cmdArgs)
if (fontName && *fontName && *fontName != '0')
{
AZStd::string fontFilePath("@devroot@/");
AZStd::string fontFilePath("@engroot@/");
fontFilePath += fontName;
fontFilePath += ".bmp";
@@ -389,7 +389,7 @@ namespace AZ
// create the full paths
char projectPath[AZ_MAX_PATH_LEN];
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN);
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
AZStd::string irradianceTextureFullPath;
AzFramework::StringFunc::Path::Join(projectPath, irradianceTextureRelativePath.c_str(), irradianceTextureFullPath, true, true);
@@ -481,7 +481,7 @@ namespace AZ
AZStd::string fullPath;
char projectPath[AZ_MAX_PATH_LEN];
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN);
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
if (!relativePath.empty())
{
@@ -529,7 +529,7 @@ namespace AZ
bool MaterialPropertyInspector::SaveMaterial() const
{
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@")) +
QString(AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@")) +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Materials" +
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
@@ -32,6 +32,23 @@ namespace AZ
{
namespace Render
{
namespace Internal
{
struct MeshComponentNotificationBusHandler final
: public MeshComponentNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
AZ_EBUS_BEHAVIOR_BINDER(
MeshComponentNotificationBusHandler, "{8B8F4977-817F-4C7C-9141-0E5FF899E1BC}", AZ::SystemAllocator, OnModelReady);
void OnModelReady(
[[maybe_unused]] const Data::Asset<RPI::ModelAsset>& modelAsset,
[[maybe_unused]] const Data::Instance<RPI::Model>& model) override
{
Call(FN_OnModelReady);
}
};
} // namespace Internal
namespace MeshComponentControllerVersionUtility
{
@@ -173,6 +190,12 @@ namespace AZ
->VirtualProperty("MinimumScreenCoverage", "GetMinimumScreenCoverage", "SetMinimumScreenCoverage")
->VirtualProperty("QualityDecayRate", "GetQualityDecayRate", "SetQualityDecayRate")
;
behaviorContext->EBus<MeshComponentNotificationBus>("MeshComponentNotificationBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "render")
->Attribute(AZ::Script::Attributes::Module, "render")
->Handler<Internal::MeshComponentNotificationBusHandler>();
}
}
@@ -306,7 +329,7 @@ namespace AZ
return model ? model->GetUvNames() : AZStd::unordered_set<AZ::Name>();
}
void MeshComponentController::OnMaterialsUpdated([[maybe_unused]] const MaterialAssignmentMap& materials)
void MeshComponentController::OnMaterialsUpdated(const MaterialAssignmentMap& materials)
{
if (m_meshFeatureProcessor)
{
@@ -321,7 +321,7 @@ namespace AZ
}
char projectPath[AZ_MAX_PATH_LEN];
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", projectPath, AZ_MAX_PATH_LEN);
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
// retrieve the source cubemap path from the configuration
// we need to make sure to use the same source cubemap for each bake
@@ -57,9 +57,8 @@ option bool o_enableMarschner_R = true;
option bool o_enableMarschner_TRT = true;
option bool o_enableMarschner_TT = true;
option bool o_enableDiffuseLobe = true;
option bool o_enableSpecularLobe = true;
option bool o_enableTransmittanceLobe = true;
option bool o_enableLongtitudeCoeff = true;
option bool o_enableAzimuthCoeff = true;
//------------------------------------------------------------------------------
// Longitudinal functions (M_R, M_TT, M_RTR)
@@ -196,8 +195,8 @@ float3 HairMarschnerBSDF(Surface surface, LightingData lightingData, const float
// R Path - single reflection from the hair towards the eye.
if (o_enableMarschner_R)
{
float lighting_R = o_enableDiffuseLobe ? M_R(surface, Lh, sinLiPlusSinLr) : 1.0f;
if (o_enableSpecularLobe)
float lighting_R = o_enableLongtitudeCoeff ? M_R(surface, Lh, sinLiPlusSinLr) : 1.0f;
if (o_enableAzimuthCoeff)
lighting_R *= N_R(surface, cos_O2, Wi, Wr, f0);
// The following lines are a cheap method to get occluded reflection by accoounting
@@ -221,8 +220,8 @@ float3 HairMarschnerBSDF(Surface surface, LightingData lightingData, const float
// on the average thickness.
if (o_enableMarschner_TT)
{
float3 lighting_TT = o_enableDiffuseLobe ? M_TT(surface, Lh, sinLiPlusSinLr) : float3(1.0f, 1.0f, 1.0f);
if (o_enableSpecularLobe)
float3 lighting_TT = o_enableLongtitudeCoeff ? M_TT(surface, Lh, sinLiPlusSinLr) : float3(1.0f, 1.0f, 1.0f);
if (o_enableAzimuthCoeff)
lighting_TT *= N_TT(surface, n2, cos_O, cos_O2, cos_Ld, f0);
// Reduce back transmittance based on the thickness of the hair
@@ -234,8 +233,8 @@ float3 HairMarschnerBSDF(Surface surface, LightingData lightingData, const float
// the hair towards the eye.
if (o_enableMarschner_TRT)
{
float3 lighting_TRT = o_enableDiffuseLobe ? M_TRT(surface, Lh, sinLiPlusSinLr) : float3(1.0f, 1.0f, 1.0f);
if (o_enableSpecularLobe)
float3 lighting_TRT = o_enableLongtitudeCoeff ? M_TRT(surface, Lh, sinLiPlusSinLr) : float3(1.0f, 1.0f, 1.0f);
if (o_enableAzimuthCoeff)
lighting_TRT *= N_TRT(surface, cos_O, cos_Ld, f0);
lighting += lighting_TRT;
}
@@ -47,9 +47,8 @@ namespace AZ
shaderOption.SetValue(AZ::Name("o_enableMarschner_R"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
shaderOption.SetValue(AZ::Name("o_enableMarschner_TRT"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });
shaderOption.SetValue(AZ::Name("o_enableMarschner_TT"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
shaderOption.SetValue(AZ::Name("o_enableDiffuseLobe"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDiffuseLobe });
shaderOption.SetValue(AZ::Name("o_enableSpecularLobe"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableSpecularLobe });
shaderOption.SetValue(AZ::Name("o_enableTransmittanceLobe"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableTransmittanceLobe });
shaderOption.SetValue(AZ::Name("o_enableLongtitudeCoeff"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
shaderOption.SetValue(AZ::Name("o_enableAzimuthCoeff"), AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });
m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
}
@@ -135,6 +135,11 @@ namespace AZ
void HairFeatureProcessor::EnablePasses(bool enable)
{
if (!m_initialized)
{
return;
}
for (auto& [passName, pass] : m_computePasses)
{
pass->SetEnabled(enable);
@@ -339,14 +344,14 @@ namespace AZ
resultSuccess &= InitPPLLFillPass();
resultSuccess &= InitPPLLResolvePass();
m_initialized = resultSuccess;
// Don't enable passes if no hair object was added yet (depending on activation order)
if (m_hairRenderObjects.empty())
if (m_initialized && m_hairRenderObjects.empty())
{
EnablePasses(false);
}
m_initialized = resultSuccess;
// this might not be an error - if the pass system is still empty / minimal
// and these passes are not part of the minimal pipeline, they will not
// be created.
@@ -21,7 +21,7 @@ namespace AZ
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<HairGlobalSettings>()
->Version(2)
->Version(3)
->Field("EnableShadows", &HairGlobalSettings::m_enableShadows)
->Field("EnableDirectionalLights", &HairGlobalSettings::m_enableDirectionalLights)
->Field("EnablePunctualLights", &HairGlobalSettings::m_enablePunctualLights)
@@ -31,9 +31,8 @@ namespace AZ
->Field("EnableMarschner_R", &HairGlobalSettings::m_enableMarschner_R)
->Field("EnableMarschner_TRT", &HairGlobalSettings::m_enableMarschner_TRT)
->Field("EnableMarschner_TT", &HairGlobalSettings::m_enableMarschner_TT)
->Field("EnableDiffuseLobe", &HairGlobalSettings::m_enableDiffuseLobe)
->Field("EnableSpecularLobe", &HairGlobalSettings::m_enableSpecularLobe)
->Field("EnableTransmittanceLobe", &HairGlobalSettings::m_enableTransmittanceLobe)
->Field("EnableLongtitudeCoeff", &HairGlobalSettings::m_enableLongtitudeCoeff)
->Field("EnableAzimuthCoeff", &HairGlobalSettings::m_enableAzimuthCoeff)
;
if (auto editContext = serializeContext->GetEditContext())
@@ -51,9 +50,8 @@ namespace AZ
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableMarschner_R, "Enable Marschner R", "Enable Marschner R.")
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableMarschner_TRT, "Enable Marschner TRT", "Enable Marschner TRT.")
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableMarschner_TT, "Enable Marschner TT", "Enable Marschner TT.")
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableDiffuseLobe, "Enable Diffuse Lobe", "Enable Diffuse Lobe.")
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableSpecularLobe, "Enable Specular Lobe", "Enable Specular Lobe.")
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableTransmittanceLobe, "Enable Transmittance Lobe", "Enable Transmittance Lobe.")
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableLongtitudeCoeff, "Enable Longtitude", "Enable Longtitude Contribution")
->DataElement(AZ::Edit::UIHandlers::Default, &HairGlobalSettings::m_enableAzimuthCoeff, "Enable Azimuth", "Enable Azimuth Contribution")
;
}
}
@@ -35,9 +35,8 @@ namespace AZ
bool m_enableMarschner_R = true;
bool m_enableMarschner_TRT = true;
bool m_enableMarschner_TT = true;
bool m_enableDiffuseLobe = true;
bool m_enableSpecularLobe = true;
bool m_enableTransmittanceLobe = true;
bool m_enableLongtitudeCoeff = true;
bool m_enableAzimuthCoeff = true;
};
} // namespace Hair
} // namespace Render
@@ -105,7 +105,7 @@ namespace AudioControlBuilder
Audio::ATLXmlTags::ATLPreloadRequestTag, "preload request"));
}
// For each preload request in the control file, determine which config group is used for this platform and register each
// For each preload request in the control file, determine which config group is used for this platform and register each
// bank listed in that preload request as a dependency.
while (preloadRequestNode)
{
@@ -163,7 +163,7 @@ namespace AudioControlBuilder
const AZ::rapidxml::xml_node<char>* configGroupNode = configGroupMap[configGroupName];
if (!configGroupNode)
{
// The config group this platform uses isn't defined in the control file. This might be intentional, so just
// The config group this platform uses isn't defined in the control file. This might be intentional, so just
// generate a warning and keep going to the next preload node.
AZ_TracePrintf("Audio Control Builder", "%s node for config group %s is not defined, so no banks are referenced.",
Audio::ATLXmlTags::ATLConfigGroupTag, configGroupName.c_str());
@@ -188,7 +188,7 @@ namespace AudioControlBuilder
}
// Prepend the bank name with the relative path to the wwise sounds folder to get relative path to the bank from
// the @assets@ alias and push that into the list of banks referenced.
// the @products@ alias and push that into the list of banks referenced.
AZStd::string soundsPrefix = Audio::Wwise::DefaultBanksPath;
banksReferenced.emplace_back(soundsPrefix + bankNameAttribute->value());
@@ -496,7 +496,7 @@ namespace AudioControlBuilder
pathDependencies.emplace(relativeBankPath, AssetBuilderSDK::ProductPathDependencyType::ProductFile);
}
// For each bank figure out what events are included in the bank, then run through every event referenced in the file and
// For each bank figure out what events are included in the bank, then run through every event referenced in the file and
// make sure it is in the list gathered from the banks.
const auto triggersNode = node->first_node(Audio::ATLXmlTags::TriggersNodeTag);
if (!triggersNode)
@@ -520,7 +520,7 @@ namespace AudioControlBuilder
AZStd::set<AZStd::string> wwiseEventsInReferencedBanks;
// Load all bankdeps files for all banks referenced and aggregate the list of events in those files.
// Load all bankdeps files for all banks referenced and aggregate the list of events in those files.
for (const AZStd::string& relativeBankPath : banksReferenced)
{
// Create the full path to the bankdeps file from the bank file.
@@ -52,7 +52,7 @@ namespace WwiseBuilder
{
fileNames.push_back(dependenciesArray[dependencyIndex].GetString());
}
// The dependency array is empty, which likely means it was modified by hand. However, every bank is dependent
// on init.bnk (other than itself), so just force add it as a dependency here. and emit a warning.
if (fileNames.size() == 0)
@@ -93,7 +93,7 @@ namespace WwiseBuilder
void WwiseBuilderWorker::Initialize()
{
AZ::IO::Path configFile("@devassets@");
AZ::IO::Path configFile("@projectroot@");
configFile /= Audio::Wwise::DefaultBanksPath;
configFile /= Audio::Wwise::ConfigFile;
@@ -180,7 +180,7 @@ namespace WwiseBuilder
{
AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Starting Job.\n");
AZ::IO::PathView fullPath(request.m_fullPath);
if (m_isShuttingDown)
{
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
@@ -204,7 +204,7 @@ namespace WwiseBuilder
AZ::Outcome<AZStd::string, AZStd::string> gatherProductDependenciesResponse = GatherProductDependencies(request.m_fullPath, request.m_sourceFile, dependencyPaths);
if (!gatherProductDependenciesResponse.IsSuccess())
{
AZ_Error(WwiseBuilderWindowName, false, "Dependency gathering for %s failed. %s",
AZ_Error(WwiseBuilderWindowName, false, "Dependency gathering for %s failed. %s",
request.m_fullPath.c_str(), gatherProductDependenciesResponse.GetError().c_str());
}
else
@@ -28,7 +28,7 @@ protected:
{
m_app.Start(AZ::ComponentApplication::Descriptor());
// 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
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
@@ -39,7 +39,7 @@ protected:
assetRoot /= "Cache";
AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", assetRoot.c_str());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", assetRoot.c_str());
}
void TearDown() override
@@ -158,7 +158,7 @@ TEST_F(WwiseBuilderTests, WwiseBuilder_InitBank_NoMetadata_NoDependencies)
TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_NoMetadata_NoDependencies)
{
// Should generate a warning after trying to find metadata for the given bank, when the bank is not the init bank.
// Should generate a warning after trying to find metadata for the given bank, when the bank is not the init bank.
// Warning should be about not being able to generate full dependency information without the metadata file.
TestSuccessCaseNoDependencies("test_doesNotExist.bnk", true);
}
@@ -180,15 +180,15 @@ TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_MultipleDependencies)
TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_DependencyArrayNonexistent_NoDependencies)
{
// Should generate a warning when trying to get dependency info from metadata file, but the dependency field does
// not an empty array. Warning should be describing that a dependency on the init bank was added by default, but
// Should generate a warning when trying to get dependency info from metadata file, but the dependency field does
// not an empty array. Warning should be describing that a dependency on the init bank was added by default, but
// the full dependency list could not be generated.
TestSuccessCaseNoDependencies("test_bank7.bnk", true);
}
TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_NoElementsInDependencyArray_NoDependencies)
{
// Should generate a warning when trying to get dependency info from metadata file, but the dependency field is
// Should generate a warning when trying to get dependency info from metadata file, but the dependency field is
// an empty array. Warning should be describing that a dependency on the init bank was added by default, but the
// full dependency list could not be generated.
TestSuccessCaseNoDependencies("test_bank8.bnk", true);
@@ -196,8 +196,8 @@ TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_NoElementsInDependencyArray_N
TEST_F(WwiseBuilderTests, WwiseBuilder_ContentBank_MissingInitBankDependency_MultipleDependencies)
{
// Should generate a warning when trying to get dependency info from metadata file, but the dependency info in the
// metadata doesn't include the init bank. Warning should be describing that a dependency on the init bank was
// Should generate a warning when trying to get dependency info from metadata file, but the dependency info in the
// metadata doesn't include the init bank. Warning should be describing that a dependency on the init bank was
// added by default.
AZStd::vector<const char*> expectedPaths = {
"Sounds/wwise/init.bnk",
@@ -231,7 +231,7 @@ namespace UnitTest
m_app.Start(AZ::ComponentApplication::Descriptor());
// 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
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
@@ -253,10 +253,10 @@ namespace UnitTest
AZ_TEST_ASSERT(serializeContext != nullptr);
Audio::Wwise::ConfigurationSettings::Reflect(serializeContext);
// Set the @assets@ alias to the path where *this* cpp file lives.
// Set the @products@ alias to the path where *this* cpp file lives.
AZStd::string rootFolder(AZ::Test::GetCurrentExecutablePath());
AZ::StringFunc::Path::Join(rootFolder.c_str(), "Test.Assets/Gems/AudioEngineWwise", rootFolder);
m_fileIO->SetAlias("@assets@", rootFolder.c_str());
m_fileIO->SetAlias("@products@", rootFolder.c_str());
// So we don't have to compute it in each test...
AZStd::string defaultBanksPath(Audio::Wwise::DefaultBanksPath);
@@ -303,26 +303,12 @@ namespace UnitTest
m_mapEntry.m_bankSubPath = "soundbanks";
config.m_platformMappings.push_back(m_mapEntry);
// Unfortunately we are writing to the config file that is simulated to be in the @assets@ subfolder
// This will cause an issue during save. Since 'm_configFilePath' is an absolute path, we need to
// reset the @assets@ alias to a meaningful assets path that does not contain any root of the m_configFilePath
// in order to write to the config file and proceed
//AZStd::string rootFolder(AZ::Test::GetCurrentExecutablePath());
//AZ::IO::Path tempAssetPath(rootFolder);
//tempAssetPath /= "Cache";
//AZStd::string previousAlias = m_fileIO->GetAlias("@assets@");
//m_fileIO->SetAlias("@assets@", tempAssetPath.c_str());
config.Save(m_configFilePath);
//m_fileIO->SetAlias("@assets@", previousAlias.c_str());
m_wwiseImpl.SetBankPaths();
//m_fileIO->SetAlias("@assets@", tempAssetPath.c_str());
m_fileIO->Remove(m_configFilePath.c_str());
//m_fileIO->SetAlias("@assets@", previousAlias.c_str());
EXPECT_STREQ(m_wwiseImpl.m_soundbankFolder.c_str(), "sounds/wwise/soundbanks/");
}
@@ -977,7 +977,7 @@ namespace Audio
, m_rPreloadRequests(rPreloadRequests)
, m_nTriggerImplIDCounter(AUDIO_TRIGGER_IMPL_ID_NUM_RESERVED)
, m_rFileCacheMgr(rFileCacheMgr)
, m_rootPath("@assets@")
, m_rootPath("@products@")
#if !defined(AUDIO_RELEASE)
, m_pDebugNameStore(nullptr)
#endif // !AUDIO_RELEASE
@@ -292,7 +292,7 @@ namespace Blast
void BlastSystemComponent::SaveConfiguration()
{
auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@");
if (!assetRoot)
{
@@ -309,7 +309,7 @@ namespace Blast
void BlastSystemComponent::CheckoutConfiguration()
{
const auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
const auto assetRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@");
AZStd::string fullPath;
AzFramework::StringFunc::Path::Join(assetRoot, DefaultConfigurationPath, fullPath);
@@ -16,7 +16,7 @@ namespace CertificateManager
{
static bool ReadFileIntoString(const char* filename, AZStd::vector<char>& outBuffer)
{
AZStd::string certificatePath = "@assets@/certificates/";
AZStd::string certificatePath = "@products@/certificates/";
certificatePath.append(filename);
AZ::IO::FileIOBase* fileBase = AZ::IO::FileIOBase::GetInstance();
@@ -58,7 +58,7 @@ namespace CertificateManager
return true;
}
FileDataSource::FileDataSource()
FileDataSource::FileDataSource()
: m_privateKeyPEM(nullptr)
, m_certificatePEM(nullptr)
, m_certificateAuthorityCertPEM(nullptr)
@@ -73,13 +73,13 @@ namespace CertificateManager
azfree(m_privateKeyPEM);
azfree(m_certificatePEM);
azfree(m_certificateAuthorityCertPEM);
}
}
void FileDataSource::ConfigureDataSource(const char* keyPath, const char* certPath, const char* caPath)
{
ConfigurePrivateKey(keyPath);
ConfigureCertificate(certPath);
ConfigureCertificateAuthority(caPath);
ConfigureCertificateAuthority(caPath);
}
void FileDataSource::ConfigurePrivateKey(const char* path)
@@ -107,7 +107,7 @@ namespace CertificateManager
if (path != nullptr)
{
LoadGenericFile(path,m_certificatePEM);
}
}
}
void FileDataSource::ConfigureCertificateAuthority(const char* path)
@@ -133,7 +133,7 @@ namespace CertificateManager
{
return m_certificateAuthorityCertPEM;
}
bool FileDataSource::HasPublicKey() const
{
return m_certificatePEM != nullptr;
@@ -143,7 +143,7 @@ namespace CertificateManager
{
return m_certificatePEM;
}
bool FileDataSource::HasPrivateKey() const
{
return m_privateKeyPEM != nullptr;
@@ -50,7 +50,7 @@ namespace EMotionFX
// Create EMotion FX allocators
Allocators::Create();
// create the new object
gEMFX = AZ::Environment::CreateVariable<EMotionFXManager*>(kEMotionFXInstanceVarName);
gEMFX.Set(EMotionFXManager::Create());
@@ -166,11 +166,11 @@ namespace EMotionFX
delete m_debugDraw;
m_debugDraw = nullptr;
m_eventManager->Destroy();
m_eventManager = nullptr;
// delete the thread datas
for (uint32 i = 0; i < m_threadDatas.size(); ++i)
{
@@ -341,7 +341,7 @@ namespace EMotionFX
void EMotionFXManager::InitAssetFolderPaths()
{
// Initialize the asset source folder path.
const char* assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
const char* assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@");
if (assetSourcePath)
{
m_assetSourceFolder = assetSourcePath;
@@ -361,12 +361,12 @@ namespace EMotionFX
}
else
{
AZ_Warning("EMotionFX", false, "Failed to set asset source path for alias '@devassets@'.");
AZ_Warning("EMotionFX", false, "Failed to set asset source path for alias '@projectroot@'.");
}
// Initialize the asset cache folder path.
const char* assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
const char* assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
if (assetCachePath)
{
m_assetCacheFolder = assetCachePath;
@@ -386,7 +386,7 @@ namespace EMotionFX
}
else
{
AZ_Warning("EMotionFX", false, "Failed to set asset cache path for alias '@assets@'.");
AZ_Warning("EMotionFX", false, "Failed to set asset cache path for alias '@products@'.");
}
}
@@ -72,13 +72,13 @@ namespace EMStudio
{
AZStd::string filename;
AZStd::string assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
AZStd::string assetCachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
AzFramework::StringFunc::AssetDatabasePath::Normalize(assetCachePath);
AZStd::string relativePath;
EBUS_EVENT_RESULT(relativePath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, assetId);
AzFramework::StringFunc::AssetDatabasePath::Join(assetCachePath.c_str(), relativePath.c_str(), filename);
return filename;
}
@@ -243,7 +243,7 @@ namespace EMStudio
void FileManager::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::TypeId sourceTypeId)
{
AZStd::string filename;
AZStd::string assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
AZStd::string assetSourcePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@projectroot@");
AzFramework::StringFunc::AssetDatabasePath::Normalize(assetSourcePath);
AzFramework::StringFunc::AssetDatabasePath::Join(assetSourcePath.c_str(), relativePath.c_str(), filename);
@@ -373,7 +373,7 @@ namespace EMStudio
const ProductAssetBrowserEntry* product = azrtti_cast<const ProductAssetBrowserEntry*>(assetBrowserEntry);
filename.clear();
AZStd::string cachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
AZStd::string cachePath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
AzFramework::StringFunc::AssetDatabasePath::Normalize(cachePath);
AzFramework::StringFunc::AssetDatabasePath::Join(cachePath.c_str(), product->GetRelativePath().c_str(), filename);
@@ -395,7 +395,7 @@ namespace EMStudio
{
return AZStd::string();
}
return filenames[0];
}
@@ -435,12 +435,12 @@ namespace EMStudio
AZStd::string result;
if (EMStudio::GetCommandManager()->ExecuteCommand(command, result))
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
"Actor <font color=green>successfully</font> saved");
}
else
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
AZStd::string::format("Actor <font color=red>failed</font> to save<br/><br/>%s", result.c_str()).c_str());
}
}
@@ -574,12 +574,12 @@ namespace EMStudio
AZStd::string result;
if (GetCommandManager()->ExecuteCommand(command, result) == false)
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
AZStd::string::format("MotionSet <font color=red>failed</font> to save<br/><br/>%s", result.c_str()).c_str());
}
else
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
"MotionSet <font color=green>successfully</font> saved");
}
}
@@ -608,7 +608,7 @@ namespace EMStudio
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::string FileManager::LoadAnimGraphFileDialog([[maybe_unused]] QWidget* parent)
{
GetManager()->SetAvoidRendering(true);
@@ -618,7 +618,7 @@ namespace EMStudio
{
return AZStd::string();
}
return filenames[0];
}
@@ -378,7 +378,7 @@ namespace EMStudio
// reset action
m_resetAction = menu->addAction(tr("&Reset"), this, &MainWindow::OnReset, QKeySequence::New);
m_resetAction->setObjectName("EMFX.MainWindow.ResetAction");
// save all
m_saveAllAction = menu->addAction(tr("Save All..."), this, &MainWindow::OnSaveAll, QKeySequence::Save);
m_saveAllAction->setObjectName("EMFX.MainWindow.SaveAllAction");
@@ -467,7 +467,7 @@ namespace EMStudio
menu->addAction("Documentation", this, []
{
QDesktopServices::openUrl(QUrl("https://o3de.org/docs/"));
});
});
menu->addAction("Forums", this, []
{
@@ -491,7 +491,7 @@ namespace EMStudio
// load preferences
PluginOptionsNotificationsBus::Router::BusRouterConnect();
LoadPreferences();
LoadPreferences();
m_autosaveTimer->setInterval(m_options.GetAutoSaveInterval() * 60 * 1000);
// Create the dirty file manager and register the workspace callback.
@@ -1072,7 +1072,7 @@ namespace EMStudio
// get only the version number of EMotion FX
AZStd::string emfxVersionString = EMotionFX::GetEMotionFX().GetVersionString();
AzFramework::StringFunc::Replace(emfxVersionString, "EMotion FX ", "", true /* case sensitive */);
// set the window title
// only set the EMotion FX version if the filename is empty
AZStd::string windowTitle;
@@ -1359,7 +1359,7 @@ namespace EMStudio
void MainWindow::LoadCharacter(const AZ::Data::AssetId& actorAssetId, const AZ::Data::AssetId& animgraphId, const AZ::Data::AssetId& motionSetId)
{
m_characterFiles.clear();
AZStd::string cachePath = gEnv->pFileIO->GetAlias("@assets@");
AZStd::string cachePath = gEnv->pFileIO->GetAlias("@products@");
AZStd::string filename;
AzFramework::StringFunc::AssetDatabasePath::Normalize(cachePath);
@@ -1543,12 +1543,12 @@ namespace EMStudio
AZStd::string result;
if (EMStudio::GetCommandManager()->ExecuteCommand(command, result))
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
"Workspace <font color=green>successfully</font> saved");
}
else
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
AZStd::string::format("Workspace <font color=red>failed</font> to save<br/><br/>%s", result.c_str()).c_str());
}
}
@@ -1575,12 +1575,12 @@ namespace EMStudio
AZStd::string result;
if (EMStudio::GetCommandManager()->ExecuteCommand(command, result))
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
"Workspace <font color=green>successfully</font> saved");
}
else
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
AZStd::string::format("Workspace <font color=red>failed</font> to save<br/><br/>%s", result.c_str()).c_str());
}
}
@@ -1644,7 +1644,7 @@ namespace EMStudio
Workspace* workspace = GetManager()->GetWorkspace();
workspace->SetDirtyFlag(true);
}
}
void MainWindow::OnReset()
{
@@ -2312,7 +2312,7 @@ namespace EMStudio
void MainWindow::Activate(const AZ::Data::AssetId& actorAssetId, const EMotionFX::AnimGraph* animGraph, const EMotionFX::MotionSet* motionSet)
{
AZStd::string cachePath = gEnv->pFileIO->GetAlias("@assets@");
AZStd::string cachePath = gEnv->pFileIO->GetAlias("@products@");
AZStd::string filename;
AzFramework::StringFunc::AssetDatabasePath::Normalize(cachePath);
@@ -2776,17 +2776,17 @@ namespace EMStudio
AZStd::string result;
if (GetCommandManager()->ExecuteCommandGroup(commandGroup, result, false))
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_SUCCESS,
"Autosave <font color=green>completed</font>");
}
else
{
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
GetNotificationWindowManager()->CreateNotificationWindow(NotificationWindow::TYPE_ERROR,
AZStd::string::format("Autosave <font color=red>failed</font><br/><br/>%s", result.c_str()).c_str());
}
}
void MainWindow::moveEvent(QMoveEvent* event)
{
MCORE_UNUSED(event);
@@ -428,7 +428,7 @@ namespace EMStudio
continue;
}
AzFramework::StringFunc::Replace(commands[i], "@assets@/", assetCacheFolder.c_str(), true /* case sensitive */);
AzFramework::StringFunc::Replace(commands[i], "@products@/", assetCacheFolder.c_str(), true /* case sensitive */);
// add the command to the command group
commandGroup->AddCommandString(commands[i]);
@@ -6,6 +6,8 @@
*
*/
#include <AzCore/Utils/Utils.h>
#include <Integration/Assets/AnimGraphAsset.h>
#include <EMotionFX/Source/Allocators.h>
#include <EMotionFX/Source/AnimGraphManager.h>
@@ -66,14 +68,10 @@ namespace EMotionFX
// through this method. Once EMotionFX is integrated to the asset system this can go away.
AZStd::string assetFilename;
EBUS_EVENT_RESULT(assetFilename, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, asset.GetId());
const char* devAssetsPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
if (devAssetsPath)
AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
if (!projectPath.empty())
{
AZStd::string assetSourcePath = devAssetsPath;
AzFramework::StringFunc::AssetDatabasePath::Normalize(assetSourcePath);
AZStd::string filename;
AzFramework::StringFunc::AssetDatabasePath::Join(assetSourcePath.c_str(), assetFilename.c_str(), filename);
AZ::IO::FixedMaxPathString filename{ (projectPath / assetFilename).LexicallyNormal().FixedMaxPathStringAsPosix() };
assetData->m_emfxAnimGraph->SetFileName(filename.c_str());
}
@@ -81,7 +79,7 @@ namespace EMotionFX
{
if (GetEMotionFX().GetIsInEditorMode())
{
AZ_Warning("EMotionFX", false, "Failed to retrieve asset source path with alias '@devassets@'. Cannot set absolute filename for '%s'", assetFilename.c_str());
AZ_Warning("EMotionFX", false, "Failed to retrieve project root path . Cannot set absolute filename for '%s'", assetFilename.c_str());
}
assetData->m_emfxAnimGraph->SetFileName(assetFilename.c_str());
}
@@ -9,6 +9,7 @@
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <Integration/Assets/MotionSetAsset.h>
#include <EMotionFX/Source/MotionSet.h>
@@ -46,7 +47,7 @@ namespace EMotionFX
const char* motionFile = entry->GetFilename();
AZ::Data::AssetId motionAssetId;
EBUS_EVENT_RESULT(motionAssetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, motionFile, azrtti_typeid<MotionAsset>(), false);
// if it failed to find it, it might be still compiling - try forcing an immediate compile:
if (!motionAssetId.IsValid())
{
@@ -149,14 +150,11 @@ namespace EMotionFX
// through this method. Once EMotionFX is integrated to the asset system this can go away.
AZStd::string assetFilename;
EBUS_EVENT_RESULT(assetFilename, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, asset.GetId());
const char* devAssetsPath = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
if (devAssetsPath)
{
AZStd::string assetSourcePath = devAssetsPath;
AZ::StringFunc::AssetDatabasePath::Normalize(assetSourcePath);
AZStd::string filename;
AZ::StringFunc::AssetDatabasePath::Join(assetSourcePath.c_str(), assetFilename.c_str(), filename);
AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
if (!projectPath.empty())
{
AZ::IO::FixedMaxPathString filename{ (projectPath / assetFilename).LexicallyNormal().FixedMaxPathStringAsPosix() };
assetData->m_emfxMotionSet->SetFilename(filename.c_str());
}
@@ -164,11 +162,11 @@ namespace EMotionFX
{
if (GetEMotionFX().GetIsInEditorMode())
{
AZ_Warning("EMotionFX", false, "Failed to retrieve asset source path with alias '@devassets@'. Cannot set absolute filename for '%s'", assetFilename.c_str());
AZ_Warning("EMotionFX", false, "Failed to retrieve project root path . Cannot set absolute filename for '%s'", assetFilename.c_str());
}
assetData->m_emfxMotionSet->SetFilename(assetFilename.c_str());
}
// now load them in:
const EMotionFX::MotionSet::MotionEntries& motionEntries = assetData->m_emfxMotionSet->GetMotionEntries();
// Get the motions in the motion set. Escalate them to the top of the build queue first so that they can be done in parallel.
@@ -179,7 +177,7 @@ namespace EMotionFX
const char* motionFilename = motionEntry->GetFilename();
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::EscalateAssetBySearchTerm, motionFilename);
}
// now that they're all escalated, the asset processor will be processing them across all threads, and we can request them one by one:
for (const auto& item : motionEntries)
{
@@ -487,9 +487,9 @@ namespace EMotionFX
return;
}
SetMediaRoot("@assets@");
// \todo Right now we're pointing at the @devassets@ location (source) and working from there, because .actor and .motion (motion) aren't yet processed through
// the scene pipeline. Once they are, we'll need to update various segments of the Tool to always read from the @assets@ cache, but write to the @devassets@ data/metadata.
SetMediaRoot("@products@");
// \todo Right now we're pointing at the @projectroot@ location (source) and working from there, because .actor and .motion (motion) aren't yet processed through
// the scene pipeline. Once they are, we'll need to update various segments of the Tool to always read from the @products@ cache, but write to the @projectroot@ data/metadata.
EMotionFX::GetEMotionFX().InitAssetFolderPaths();
// Register EMotionFX event handler
@@ -25,11 +25,11 @@ namespace EMotionFX
ExecuteCommands({
R"str(CreateMotionSet -name MotionSet0)str",
R"str(CreateMotionSet -name MotionSet1)str",
R"str(MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str",
R"str(MotionSetAddMotion -motionSetID 1 -motionFilenamesAndIds @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str",
R"str(MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str",
R"str(MotionSetAddMotion -motionSetID 1 -motionFilenamesAndIds @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle)str",
R"str(MotionSetRemoveMotion -motionSetID 0 -motionIds rin_idle)str",
R"str(RemoveMotionSet -motionSetID 0)str",
R"str(RemoveMotion -filename @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion)str",
R"str(RemoveMotion -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion)str",
});
EMStudio::MotionSetsWindowPlugin* motionSetsWindowPlugin = static_cast<EMStudio::MotionSetsWindowPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::MotionSetsWindowPlugin::CLASS_ID));
@@ -33,7 +33,7 @@ namespace EMotionFX
ASSERT_TRUE(motionSet) << "Motion set with id 0 does not exist";
motionSetsWindowPlugin->SetSelectedSet(motionSet);
const std::string filename = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion";
const std::string filename = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion";
ExecuteCommands({
"ImportMotion -filename " + filename,
"MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds " + filename + ";rin_idle",
@@ -55,7 +55,7 @@ namespace EMotionFX
// By using this mock catalog, we can pretend to load the specific referenced assets without actually loading anything.
UnitTest::MockLoadAssetCatalogAndHandler testAssetCatalog({ referencedAnimGraph, referencedMotionSet });
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExample.animgraph";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExample.animgraph";
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
EMotionFXBuilder::AnimGraphBuilderWorker builderWorker;
@@ -68,7 +68,7 @@ namespace EMotionFX
TEST_F(EMotionFXBuilderTests, TestAnimGraphAsset_NoDependency_OutputNoProductDependencies)
{
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExampleNoDependency.animgraph";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/AnimGraphExampleNoDependency.animgraph";
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
EMotionFXBuilder::AnimGraphBuilderWorker builderWorker;
@@ -78,7 +78,7 @@ namespace EMotionFX
TEST_F(EMotionFXBuilderTests, TestAnimGraphAsset_InvalidFilePath_OutputNoProductDependencies)
{
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.animgraph";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.animgraph";
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
EMotionFXBuilder::AnimGraphBuilderWorker builderWorker;
@@ -88,7 +88,7 @@ namespace EMotionFX
TEST_F(EMotionFXBuilderTests, TestAnimGraphAsset_EmptyFile_OutputNoProductDependencies)
{
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyAnimGraphExample.animgraph";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyAnimGraphExample.animgraph";
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
EMotionFXBuilder::AnimGraphBuilderWorker builderWorker;
@@ -100,7 +100,7 @@ namespace EMotionFX
TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_HasReferenceNode_OutputProductDependencies)
{
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset";
ProductPathDependencySet productDependencies;
EMotionFXBuilder::MotionSetBuilderWorker builderWorker;
@@ -112,7 +112,7 @@ namespace EMotionFX
TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_NoDependency_OutputNoProductDependencies)
{
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExampleNoDependency.motionset";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExampleNoDependency.motionset";
ProductPathDependencySet productDependencies;
EMotionFXBuilder::MotionSetBuilderWorker builderWorker;
@@ -122,7 +122,7 @@ namespace EMotionFX
TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_InvalidFilePath_OutputNoProductDependencies)
{
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.motionset";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/InvalidPathExample.motionset";
ProductPathDependencySet productDependencies;
EMotionFXBuilder::MotionSetBuilderWorker builderWorker;
@@ -132,7 +132,7 @@ namespace EMotionFX
TEST_F(EMotionFXBuilderTests, TestMotionSetAsset_EmptyFile_OutputNoProductDependencies)
{
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyMotionSetExample.motionset";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/EmptyMotionSetExample.motionset";
ProductPathDependencySet productDependencies;
EMotionFXBuilder::MotionSetBuilderWorker builderWorker;
@@ -124,7 +124,7 @@ namespace EMotionFX
{
using testing::_;
const AZStd::string fileName = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset";
const AZStd::string fileName = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/EMotionFXBuilderTestAssets/MotionSetExample.motionset";
MockAssetSystemRequests assetSystem;
EXPECT_CALL(assetSystem, CompileAssetSync(_))
@@ -302,7 +302,7 @@ namespace EMotionFX
GetEMotionFX().SetMediaRootFolder(assetFolder.c_str());
GetEMotionFX().InitAssetFolderPaths();
const char* actorFilename = "@assets@\\animationsamples\\advanced_rinlocomotion\\actor\\rinactor.actor";
const char* actorFilename = "@products@\\animationsamples\\advanced_rinlocomotion\\actor\\rinactor.actor";
Importer* importer = GetEMotionFX().GetImporter();
importer->SetLoggingEnabled(false);
@@ -402,9 +402,9 @@ namespace EMotionFX
// This path points to assets in the advance rin demo.
// To test different assets, change the path here.
const char* actorFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor";
const char* motionSetFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset";
const char* animGraphFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph";
const char* actorFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor";
const char* motionSetFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset";
const char* animGraphFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph";
Importer* importer = GetEMotionFX().GetImporter();
importer->SetLoggingEnabled(false);
@@ -714,9 +714,9 @@ namespace EMotionFX
// This path points to assets in the advance rin demo.
// To test different assets, change the path here.
const char* actorFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor";
const char* motionSetFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset";
const char* animGraphFilename = "@assets@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph";
const char* actorFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\Actor\\rinActor.actor";
const char* motionSetFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.motionset";
const char* animGraphFilename = "@products@\\AnimationSamples\\Advanced_RinLocomotion\\AnimationEditorFiles\\Advanced_RinLocomotion.animgraph";
Importer* importer = GetEMotionFX().GetImporter();
importer->SetLoggingEnabled(false);
@@ -772,13 +772,13 @@ namespace EMotionFX
TEST_F(PerformanceTestFixture, DISABLED_MotionSamplingPerformanceNonUniform)
{
// Make sure that the motion is set to use NonUniform sampling! Change this in the scene settings! Otherwise you get wrong results.
TestMotionSamplingPerformance("@assets@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_idle.motion");
TestMotionSamplingPerformance("@products@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_idle.motion");
}
TEST_F(PerformanceTestFixture, DISABLED_MotionSamplingPerformanceUniform)
{
// Make sure that the motion is set to use Uniform sampling! Change this in the scene settings! Otherwise you get wrong results.
TestMotionSamplingPerformance("@assets@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_walk_kick_01.motion");
TestMotionSamplingPerformance("@products@\\animationsamples\\advanced_rinlocomotion\\motions\\rin_walk_kick_01.motion");
}
} // namespace EMotionFX
@@ -33,7 +33,7 @@ namespace EMotionFX
ASSERT_EQ(GetActorManager().GetNumActors(), 0);
// Load an Actor
const char* actorCmd{ "ImportActor -filename @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor" };
const char* actorCmd{ "ImportActor -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor" };
{
AZStd::string result;
EXPECT_TRUE(CommandSystem::GetCommandManager()->ExecuteCommand(actorCmd, result)) << result.c_str();
@@ -297,16 +297,16 @@ namespace EMotionFX
INSTANTIATE_TEST_CASE_P(Integ_TestPoses, INTEG_PoseComparisonFixture,
::testing::Values(
PoseComparisonFixtureParams (
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
),
PoseComparisonFixtureParams (
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.actor",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.animgraph",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.motionset",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.emfxrecording"
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.actor",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.animgraph",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.motionset",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.emfxrecording"
)
)
);
@@ -314,10 +314,10 @@ namespace EMotionFX
INSTANTIATE_TEST_CASE_P(Integ_TestPoseComparison, INTEG_TestPoseComparisonFixture,
::testing::Values(
PoseComparisonFixtureParams (
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@assets@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
)
)
);
@@ -30,12 +30,12 @@ namespace EMotionFX
motionSetsWindowPlugin->SetSelectedSet(motionSet);
ExecuteCommands({
"ImportMotion -filename @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion",
"MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle"
"ImportMotion -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion",
"MotionSetAddMotion -motionSetID 0 -motionFilenamesAndIds @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion;rin_idle"
});
char resolvedPath[AZ::IO::MaxPathLength];
EXPECT_TRUE(AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion", resolvedPath, AZ_ARRAY_SIZE(resolvedPath)));
EXPECT_TRUE(AZ::IO::FileIOBase::GetInstance()->ResolvePath("@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion", resolvedPath, AZ_ARRAY_SIZE(resolvedPath)));
m_motionFileName = resolvedPath;
AzFramework::ApplicationRequests::Bus::Broadcast([](AzFramework::ApplicationRequests* requests, AZStd::string& path) { requests->NormalizePathKeepCase(path); }, m_motionFileName);
m_motionName = "rin_idle";
@@ -44,7 +44,7 @@ namespace EMotionFX
RecordProperty("test_case_id", "C16302179");
const AZStd::string motionAsset("@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion");
const AZStd::string motionAsset("@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion");
const AZStd::string createAnimGraphCmd("CreateAnimGraph");
const AZStd::string motionSetName("TestMotionSet");
const AZStd::string createMotionSetCmd("CreateMotionSet -motionSetID 42 -name " + motionSetName);
@@ -36,7 +36,7 @@ namespace EMotionFX
RecordProperty("test_case_id", "C1559124");
const QString assetName = "rin_idle"; // Asset name to appear in table
const AZStd::string motionCmd = AZStd::string::format("ImportMotion -filename @devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion");
const AZStd::string motionCmd = AZStd::string::format("ImportMotion -filename @engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion");
auto motionWindowPlugin = static_cast<EMStudio::MotionWindowPlugin*>(EMStudio::GetPluginManager()->FindActivePlugin(EMStudio::MotionWindowPlugin::CLASS_ID));
ASSERT_TRUE(motionWindowPlugin) << "Could not find the Motion Window Plugin";
@@ -38,7 +38,7 @@ namespace EMotionFX
EXPECT_EQ(table->rowCount(), 1) << "Expected the table to have no rows yet";
// Create actor and actor instance.
const char* actorFilename = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor";
const char* actorFilename = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor";
AZStd::unique_ptr<EMotionFX::Actor> m_actor = EMotionFX::GetImporter().LoadActor(actorFilename);
EXPECT_TRUE(m_actor.get() != nullptr) << "Actor not loaded.";
EMotionFX::ActorInstance* m_actorInstance = ActorInstance::Create(m_actor.get());
@@ -26,7 +26,7 @@ namespace EMotionFX
EMStudio::GetMainWindow()->ApplicationModeChanged("AnimGraph");
// Load Rin anim graph.
const char* rinGraph = "@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph";
const char* rinGraph = "@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph";
const AZStd::string rinGraphPath = ResolvePath(rinGraph);
AZStd::string command = AZStd::string::format("LoadAnimGraph -filename \"%s\"", rinGraphPath.c_str());
AZStd::string result;
@@ -551,7 +551,7 @@ namespace EMotionFX
QString GetTestMotionFileName() const
{
AZStd::string resolvedAssetPath = this->ResolvePath("@devroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion");
AZStd::string resolvedAssetPath = this->ResolvePath("@engroot@/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin_idle.motion");
return QString::fromUtf8(resolvedAssetPath.data(), aznumeric_cast<int>(resolvedAssetPath.size()));
}
@@ -59,7 +59,7 @@ The API:
- ExecuteByString(string) runs a string buffer in the Python VM; it returns no value
- ExecuteByFilename(string) loads a file off of the disk to execute in the
Python VM; the call returns no value. The filename can contain an alias such as
\@devroot\@ to execute a project relative file inside the Editor
\@projectroot\@ to execute a project relative file inside the Editor
#### New Console Commands

Some files were not shown because too many files have changed in this diff Show More