+#include
namespace O3DE::ProjectManager
{
+ inline constexpr static int s_contentMargins = 80;
+ inline constexpr static int s_buttonSpacing = 30;
+ inline constexpr static int s_iconSize = 24;
+ inline constexpr static int s_spacerSize = 20;
+ inline constexpr static int s_boxButtonWidth = 210;
+ inline constexpr static int s_boxButtonHeight = 280;
+
FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent)
: ScreenWidget(parent)
- , m_ui(new Ui::FirstTimeUseClass())
{
- m_ui->setupUi(this);
+ QVBoxLayout* vLayout = new QVBoxLayout();
+ setLayout(vLayout);
+ vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins);
+
+ QLabel* titleLabel = new QLabel(this);
+ titleLabel->setText(tr("Ready. Set. Create!"));
+ titleLabel->setStyleSheet("font-size: 60px");
+ vLayout->addWidget(titleLabel);
+
+ QLabel* introLabel = new QLabel(this);
+ introLabel->setTextFormat(Qt::AutoText);
+ introLabel->setText(tr("Welcome to O3DE! Start something new by creating a project. Not sure what to create?
Explore what\342\200\231s available by downloading our sample project.
"));
+ introLabel->setStyleSheet("font-size: 14px");
+ vLayout->addWidget(introLabel);
+
+ QHBoxLayout* buttonLayout = new QHBoxLayout();
+ buttonLayout->setSpacing(s_buttonSpacing);
+
+ m_createProjectButton = CreateLargeBoxButton(QIcon(":/Resources/Add.svg"), tr("Create Project"), this);
+ m_createProjectButton->setIconSize(QSize(s_iconSize, s_iconSize));
+ buttonLayout->addWidget(m_createProjectButton);
+
+ m_addProjectButton = CreateLargeBoxButton(QIcon(":/Resources/Select_Folder.svg"), tr("Add a Project"), this);
+ m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize));
+ buttonLayout->addWidget(m_addProjectButton);
+
+ QSpacerItem* buttonSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum);
+ buttonLayout->addItem(buttonSpacer);
+
+ vLayout->addItem(buttonLayout);
- connect(m_ui->createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton);
- connect(m_ui->openProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleOpenProjectButton);
+ QSpacerItem* verticalSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ vLayout->addItem(verticalSpacer);
+
+ // Using border-image allows for scaling options background-image does not support
+ setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Resources/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }");
+
+ connect(m_createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton);
+ connect(m_addProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleAddProjectButton);
}
ProjectManagerScreen FirstTimeUseScreen::GetScreenEnum()
@@ -36,9 +82,21 @@ namespace O3DE::ProjectManager
emit ResetScreenRequest(ProjectManagerScreen::NewProjectSettingsCore);
emit ChangeScreenRequest(ProjectManagerScreen::NewProjectSettingsCore);
}
- void FirstTimeUseScreen::HandleOpenProjectButton()
+ void FirstTimeUseScreen::HandleAddProjectButton()
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
}
+ QPushButton* FirstTimeUseScreen::CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent)
+ {
+ QPushButton* largeBoxButton = new QPushButton(icon, text, parent);
+
+ largeBoxButton->setFixedSize(s_boxButtonWidth, s_boxButtonHeight);
+ largeBoxButton->setFlat(true);
+ largeBoxButton->setFocusPolicy(Qt::FocusPolicy::NoFocus);
+ largeBoxButton->setStyleSheet("QPushButton { font-size: 14px; background-color: rgba(0, 0, 0, 191); }");
+
+ return largeBoxButton;
+ }
+
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h
index 4b4a99f16a..b6b57dc16b 100644
--- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h
+++ b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h
@@ -15,10 +15,8 @@
#include
#endif
-namespace Ui
-{
- class FirstTimeUseClass;
-}
+QT_FORWARD_DECLARE_CLASS(QIcon)
+QT_FORWARD_DECLARE_CLASS(QPushButton)
namespace O3DE::ProjectManager
{
@@ -32,10 +30,13 @@ namespace O3DE::ProjectManager
protected slots:
void HandleNewProjectButton();
- void HandleOpenProjectButton();
+ void HandleAddProjectButton();
private:
- QScopedPointer m_ui;
+ QPushButton* CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr);
+
+ QPushButton* m_createProjectButton;
+ QPushButton* m_addProjectButton;
};
} // namespace O3DE::ProjectManager
diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.ui b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.ui
deleted file mode 100644
index fdc195731f..0000000000
--- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.ui
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
- FirstTimeUseClass
-
-
-
- 0
- 0
- 881
- 555
-
-
-
- Form
-
-
- -
-
-
-
-
-
-
- 30
-
-
-
- READY. SET. CREATE!
-
-
-
- -
-
-
- <html><head/><body><p>Welcome to O3DE! Start something new by creating a project. Not sure what to create? </p><p>Explore what’s available by downloading our sample project.</p></body></html>
-
-
- Qt::AutoText
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Create Project
-
-
-
- :/Resources/Add.svg:/Resources/Add.svg
-
-
-
- 16
- 16
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Open a Project
-
-
-
- :/Resources/Select_Folder.svg:/Resources/Select_Folder.svg
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp
index 977667071f..6b9d268564 100644
--- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp
+++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp
@@ -27,6 +27,12 @@ namespace O3DE::ProjectManager
, m_ui(new Ui::ProjectManagerWindowClass())
{
m_ui->setupUi(this);
+ QLayout* layout = m_ui->centralWidget->layout();
+ layout->setMargin(0);
+ layout->setSpacing(0);
+ layout->setContentsMargins(0, 0, 0, 0);
+
+ setFixedSize(this->geometry().width(), this->geometry().height());
m_pythonBindings = AZStd::make_unique(engineRootPath);
diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui
index 789dd1b656..a71ed3aabf 100644
--- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui
+++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui
@@ -6,10 +6,16 @@
0
0
- 800
- 600
+ 1200
+ 800
+
+
+ 0
+ 0
+
+
O3DE Project Manager
@@ -21,7 +27,7 @@
0
0
- 800
+ 1200
36
diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h
index ae235daf2b..483066e031 100644
--- a/Code/Tools/ProjectManager/Source/ScreenWidget.h
+++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h
@@ -15,18 +15,20 @@
#include
#include
+#include
+#include
#endif
namespace O3DE::ProjectManager
{
class ScreenWidget
- : public QWidget
+ : public QFrame
{
Q_OBJECT
public:
explicit ScreenWidget(QWidget* parent = nullptr)
- : QWidget(parent)
+ : QFrame(parent)
{
}
~ScreenWidget() = default;
diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp
index 69af09f496..b8a38ed155 100644
--- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp
+++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp
@@ -22,6 +22,9 @@ namespace O3DE::ProjectManager
: QWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
+ vLayout->setMargin(0);
+ vLayout->setSpacing(0);
+ vLayout->setContentsMargins(0, 0, 0, 0);
setLayout(vLayout);
m_screenStack = new QStackedWidget();
diff --git a/Code/Tools/ProjectManager/project_manager.qrc b/Code/Tools/ProjectManager/project_manager.qrc
index 6509a9f940..3c23bc24ff 100644
--- a/Code/Tools/ProjectManager/project_manager.qrc
+++ b/Code/Tools/ProjectManager/project_manager.qrc
@@ -9,5 +9,6 @@
Resources/iOS.svg
Resources/Linux.svg
Resources/macOS.svg
+ Resources/Backgrounds/FirstTimeBackgroundImage.jpg
diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake
index 9ffdb6029d..3594d1e079 100644
--- a/Code/Tools/ProjectManager/project_manager_files.cmake
+++ b/Code/Tools/ProjectManager/project_manager_files.cmake
@@ -22,7 +22,6 @@ set(FILES
Source/EngineInfo.cpp
Source/FirstTimeUseScreen.h
Source/FirstTimeUseScreen.cpp
- Source/FirstTimeUseScreen.ui
Source/ProjectManagerWindow.h
Source/ProjectManagerWindow.cpp
Source/ProjectTemplateInfo.h
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp
index 3dc14814de..d2818f3653 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp
@@ -41,18 +41,6 @@ namespace AZ
static AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler* g_fbxImporter = nullptr;
static AZStd::vector g_componentDescriptors;
- void Initialize()
- {
- // Currently it's still needed to explicitly create an instance of this instead of letting
- // it be a normal component. This is because ResourceCompilerScene needs to return
- // the list of available extensions before it can start the application.
- if (!g_fbxImporter)
- {
- g_fbxImporter = aznew AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler();
- g_fbxImporter->Activate();
- }
- }
-
void Reflect(AZ::SerializeContext* /*context*/)
{
// Descriptor registration is done in Reflect instead of Initialize because the ResourceCompilerScene initializes the libraries before
@@ -64,6 +52,7 @@ namespace AZ
{
// Global importer and behavior
g_componentDescriptors.push_back(FbxSceneBuilder::FbxImporter::CreateDescriptor());
+ g_componentDescriptors.push_back(FbxSceneImporter::FbxImportRequestHandler::CreateDescriptor());
// Node and attribute importers
g_componentDescriptors.push_back(AssImpBitangentStreamImporter::CreateDescriptor());
@@ -125,7 +114,6 @@ namespace AZ
extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env)
{
AZ::Environment::Attach(static_cast(env));
- AZ::SceneAPI::FbxSceneBuilder::Initialize();
}
extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext* context)
{
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp
index 155209f1b5..a43f1e16b8 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp
@@ -10,12 +10,16 @@
*
*/
+#include
+#include
#include
-#include
+#include
+#include
+#include
+#include
#include
#include
#include
-#include
namespace AZ
{
@@ -23,10 +27,25 @@ namespace AZ
{
namespace FbxSceneImporter
{
- const char* FbxImportRequestHandler::s_extension = ".fbx";
+ void SceneImporterSettings::Reflect(AZ::ReflectContext* context)
+ {
+ if (auto serializeContext = azrtti_cast(context); serializeContext)
+ {
+ serializeContext->Class()
+ ->Version(1)
+ ->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions);
+ }
+ }
void FbxImportRequestHandler::Activate()
{
+ auto settingsRegistry = AZ::SettingsRegistry::Get();
+
+ if (settingsRegistry)
+ {
+ settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter");
+ }
+
BusConnect();
}
@@ -37,21 +56,29 @@ namespace AZ
void FbxImportRequestHandler::Reflect(ReflectContext* context)
{
+ SceneImporterSettings::Reflect(context);
+
SerializeContext* serializeContext = azrtti_cast(context);
if (serializeContext)
{
- serializeContext->Class()->Version(1);
+ serializeContext->Class()->Version(1)->Attribute(
+ AZ::Edit::Attributes::SystemComponentTags,
+ AZStd::vector({AssetBuilderSDK::ComponentTags::AssetBuilder}));
+
}
}
void FbxImportRequestHandler::GetSupportedFileExtensions(AZStd::unordered_set& extensions)
{
- extensions.insert(s_extension);
+ extensions.insert(m_settings.m_supportedFileTypeExtensions.begin(), m_settings.m_supportedFileTypeExtensions.end());
}
Events::LoadingResult FbxImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, [[maybe_unused]] RequestingApplication requester)
{
- if (!AzFramework::StringFunc::Path::IsExtension(path.c_str(), s_extension))
+ AZStd::string extension;
+ StringFunc::Path::GetExtension(path.c_str(), extension);
+
+ if (!m_settings.m_supportedFileTypeExtensions.contains(extension))
{
return Events::LoadingResult::Ignored;
}
@@ -73,6 +100,11 @@ namespace AZ
return Events::LoadingResult::AssetFailure;
}
}
+
+ void FbxImportRequestHandler::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
+ {
+ provided.emplace_back(AZ_CRC_CE("AssetImportRequestHandler"));
+ }
} // namespace Import
} // namespace SceneAPI
} // namespace AZ
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h
index 8b33051f1e..12c7c6f877 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h
@@ -21,12 +21,21 @@ namespace AZ
{
namespace FbxSceneImporter
{
+ struct SceneImporterSettings
+ {
+ AZ_TYPE_INFO(SceneImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}");
+
+ static void Reflect(AZ::ReflectContext* context);
+
+ AZStd::unordered_set m_supportedFileTypeExtensions;
+ };
+
class FbxImportRequestHandler
- : public SceneCore::BehaviorComponent
+ : public AZ::Component
, public Events::AssetImportRequestBus::Handler
{
public:
- AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}", SceneCore::BehaviorComponent);
+ AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}");
~FbxImportRequestHandler() override = default;
@@ -38,8 +47,13 @@ namespace AZ
Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid,
RequestingApplication requester) override;
+ static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
+
private:
- static const char* s_extension;
+
+ SceneImporterSettings m_settings;
+
+ static constexpr const char* SettingsFilename = "AssetImporterSettings.json";
};
} // namespace FbxSceneImporter
} // namespace SceneAPI
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp
index 0ee25195bc..38f7de89c6 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp
@@ -260,7 +260,7 @@ namespace AZ
{
AZ_TraceContext("Importer", "Animation");
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
// Add check for animation layers at the scene level.
@@ -387,11 +387,10 @@ namespace AZ
}
Events::ProcessingResultCombiner combinedAnimationResult;
- for (AZ::u32 meshIndex = 0; meshIndex < currentNode->mNumMeshes; ++meshIndex)
+ if (context.m_sourceNode.ContainsMesh())
{
- aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[meshIndex]];
-
- if (NodeToChannelToMorphAnim::iterator channelsForMeshName = meshMorphAnimations.find(mesh->mName.C_Str());
+ const aiMesh* firstMesh = scene->mMeshes[currentNode->mMeshes[0]];
+ if (NodeToChannelToMorphAnim::iterator channelsForMeshName = meshMorphAnimations.find(firstMesh->mName.C_Str());
channelsForMeshName != meshMorphAnimations.end())
{
const auto [nodeIterName, channels] = *channelsForMeshName;
@@ -399,7 +398,7 @@ namespace AZ
{
const auto& [animation, morphAnimation] = animAndMorphAnim;
combinedAnimationResult += ImportBlendShapeAnimation(
- context, animation, morphAnimation, mesh);
+ context, animation, morphAnimation, firstMesh);
}
}
}
@@ -413,32 +412,39 @@ namespace AZ
if (boneAnimations.empty() && !meshMorphAnimations.empty())
{
const aiAnimation* animation = scene->mAnimations[0];
-
- // Morph animations need a regular animation on the node, as well.
- // If there is no bone animation on the current node, then generate one here.
- AZStd::shared_ptr createdAnimationData =
- AZStd::make_shared();
-
- const size_t numKeyframes = animation->mDuration + 1; // +1 because we start at 0 and the last keyframe is at mDuration instead of mDuration-1
- createdAnimationData->ReserveKeyFrames(numKeyframes);
-
- const double timeStepBetweenFrames = 1.0 / animation->mTicksPerSecond;
- createdAnimationData->SetTimeStepBetweenFrames(timeStepBetweenFrames);
-
- // Set every frame of the animation to the start location of the node.
- aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode);
- DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
- context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
- context.m_sourceSceneSystem.ConvertUnit(localTransform);
- for (AZ::u32 time = 0; time <= animation->mDuration; ++time)
+ for (AZ::u32 channelIndex = 0; channelIndex < animation->mNumMorphMeshChannels; ++channelIndex)
{
- createdAnimationData->AddKeyFrame(localTransform);
- }
-
- Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
- context.m_currentGraphPosition, nodeName.c_str(), AZStd::move(createdAnimationData));
- context.m_scene.GetGraph().MakeEndPoint(addNode);
+ const aiMeshMorphAnim* nodeAnim = animation->mMorphMeshChannels[channelIndex];
+ // Morph animations need a regular animation on the node, as well.
+ // If there is no bone animation on the current node, then generate one here.
+ AZStd::shared_ptr createdAnimationData =
+ AZStd::make_shared();
+
+ const size_t numKeyframes = GetNumKeyFrames(
+ nodeAnim->mNumKeys,
+ animation->mDuration,
+ animation->mTicksPerSecond);
+ createdAnimationData->ReserveKeyFrames(numKeyframes);
+
+ const double timeStepBetweenFrames = 1.0 / animation->mTicksPerSecond;
+ createdAnimationData->SetTimeStepBetweenFrames(timeStepBetweenFrames);
+
+ // Set every frame of the animation to the start location of the node.
+ aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode);
+ DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
+ context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
+ context.m_sourceSceneSystem.ConvertUnit(localTransform);
+ for (AZ::u32 time = 0; time <= numKeyframes; ++time)
+ {
+ createdAnimationData->AddKeyFrame(localTransform);
+ }
+ const AZStd::string stubBoneAnimForMorphName(AZStd::string::format("%s%s", nodeName.c_str(), nodeAnim->mName.C_Str()));
+ Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
+ context.m_currentGraphPosition, stubBoneAnimForMorphName.c_str(), AZStd::move(createdAnimationData));
+ context.m_scene.GetGraph().MakeEndPoint(addNode);
+ }
+
return combinedAnimationResult.GetResult();
}
decltype(boneAnimations) parentFillerAnimations;
@@ -446,8 +452,8 @@ namespace AZ
// Go through all the animations and make sure we create animations for bones who's parents don't have an animation
for (auto&& anim : boneAnimations)
{
- aiNode* node = scene->mRootNode->FindNode(anim.first.c_str());
- aiNode* parent = node->mParent;
+ const aiNode* node = scene->mRootNode->FindNode(anim.first.c_str());
+ const aiNode* parent = node->mParent;
while (parent && parent != scene->mRootNode)
{
@@ -598,7 +604,8 @@ namespace AZ
// Keyframes generated for every single frame of the animation.
typedef AZStd::map> ValueToKeyDataMap;
ValueToKeyDataMap valueToKeyDataMap;
-
+ // Key time can be less than zero, normalize to have zero be the lowest time.
+ double keyOffset = 0;
for (int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++)
{
aiMeshMorphKey& key = meshMorphAnim->mKeys[keyIdx];
@@ -609,6 +616,10 @@ namespace AZ
valueToKeyDataMap[currentValue].insert(
AZStd::upper_bound(valueToKeyDataMap[currentValue].begin(), valueToKeyDataMap[currentValue].end(),thisKey),
thisKey);
+ if (key.mTime < keyOffset)
+ {
+ keyOffset = key.mTime;
+ }
}
}
@@ -631,7 +642,7 @@ namespace AZ
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
float weight = 0;
- if (!SampleKeyFrame(weight, keys, keys.size(), time, keyIdx))
+ if (!SampleKeyFrame(weight, keys, keys.size(), time + keyOffset, keyIdx))
{
return Events::ProcessingResult::Failure;
}
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp
index c2b1f20035..2ce9bc14f4 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp
@@ -25,7 +25,6 @@
#include
#include
-
namespace AZ
{
namespace SceneAPI
@@ -44,7 +43,7 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast(context);
if (serializeContext)
{
- serializeContext->Class()->Version(2); // LYN-2576
+ serializeContext->Class()->Version(3); // LYN-3250
}
}
@@ -55,62 +54,79 @@ namespace AZ
{
return Events::ProcessingResult::Ignored;
}
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
- GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context));
- if (!meshDataResult.IsSuccess())
+ const auto meshHasTangentsAndBitangents = [&scene](const unsigned int meshIndex)
{
- return meshDataResult.GetError();
- }
- const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue());
-
- size_t vertexCount = parentMeshData->GetVertexCount();
+ return scene->mMeshes[meshIndex]->HasTangentsAndBitangents();
+ };
- int sdkMeshIndex = parentMeshData->GetSdkMeshIndex();
- if (sdkMeshIndex < 0 || sdkMeshIndex >= currentNode->mNumMeshes)
+ // If there are no bitangents on any meshes, there's nothing to import in this function.
+ const bool anyMeshHasTangentsAndBitangents = AZStd::any_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
+ if (!anyMeshHasTangentsAndBitangents)
{
- AZ_Error(Utilities::ErrorWindow, false,
- "Tried to construct bitangent stream attribute for invalid or non-mesh parent data, mesh index is invalid");
- return Events::ProcessingResult::Failure;
+ return Events::ProcessingResult::Ignored;
}
- aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
-
- if (!mesh->HasTangentsAndBitangents())
+ // AssImp nodes with multiple meshes on them occur when AssImp split a mesh on material.
+ // This logic recombines those meshes to minimize the changes needed to replace FBX SDK with AssImp, FBX SDK did not separate meshes,
+ // and the engine has code to do this later.
+ const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
+ if (!allMeshesHaveTangentsAndBitangents)
{
- return Events::ProcessingResult::Ignored;
+ const char* mixedBitangentsError =
+ "Node with name %s has meshes with and without bitangents. "
+ "Placeholder incorrect bitangents will be generated to allow the data to process, "
+ "but the source art needs to be fixed to correct this. Either apply bitangents to all meshes on this node, "
+ "or remove all bitangents from all meshes on this node.";
+ AZ_Error(
+ Utilities::ErrorWindow, false, mixedBitangentsError, currentNode->mName.C_Str());
}
+ const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
+
AZStd::shared_ptr bitangentStream =
AZStd::make_shared();
-
// AssImp only has one bitangentStream per mesh.
bitangentStream->SetBitangentSetIndex(0);
bitangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
bitangentStream->ReserveContainerSpace(vertexCount);
-
- for (int v = 0; v < mesh->mNumVertices; ++v)
+ for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
- const Vector3 bitangent(
- AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mBitangents[v]));
- bitangentStream->AppendBitangent(bitangent);
+ const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
+
+ for (int v = 0; v < mesh->mNumVertices; ++v)
+ {
+ if (!mesh->HasTangentsAndBitangents())
+ {
+ // This node has mixed meshes with and without bitangents.
+ // An error was already thrown above. Output stub bitangents so
+ // the mesh can still be output in some form, even if the data isn't correct.
+ // The bitangent count needs to match the vertex count on the associated mesh node.
+ bitangentStream->AppendBitangent(Vector3::CreateAxisY());
+ }
+ else
+ {
+ const Vector3 bitangent(
+ AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mBitangents[v]));
+ bitangentStream->AppendBitangent(bitangent);
+ }
+ }
}
- AZStd::string nodeName(AZStd::string::format("%s",m_defaultNodeName));
Containers::SceneGraph::NodeIndex newIndex =
- context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
+ context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName);
Events::ProcessingResult bitangentResults;
- AssImpSceneAttributeDataPopulatedContext dataPopulated(context, bitangentStream, newIndex, nodeName.c_str());
+ AssImpSceneAttributeDataPopulatedContext dataPopulated(context, bitangentStream, newIndex, m_defaultNodeName);
bitangentResults = Events::Process(dataPopulated);
if (bitangentResults != Events::ProcessingResult::Failure)
{
bitangentResults = AddAttributeDataNodeWithContexts(dataPopulated);
}
-
return bitangentResults;
}
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp
index c0399329d3..34266a3e29 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp
@@ -74,37 +74,51 @@ namespace AZ
{
return meshDataResult.GetError();
}
- const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue());
- int parentMeshIndex = parentMeshData->GetSdkMeshIndex();
Events::ProcessingResultCombiner combinedBlendShapeResult;
+ // 1. Loop through meshes & anims
+ // Create storage: Anim to meshes
+ // 2. Loop through anims & meshes
+ // Create an anim mesh for each anim, with meshes re-combined.
+ // AssImp separates meshes that have multiple materials.
+ // This code re-combines them to match previous FBX SDK behavior,
+ // so they can be separated by engine code instead.
+ AZStd::map>> animToMeshToAnimMeshIndices;
for (int nodeMeshIdx = 0; nodeMeshIdx < numMesh; nodeMeshIdx++)
{
int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[nodeMeshIdx];
const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx];
-
- // Each mesh gets its own node in the scene graph, so only generate
- // morph targets for the current mesh.
- if (parentMeshIndex != nodeMeshIdx || !aiMesh->mNumAnimMeshes)
+ for (int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
{
- continue;
+ aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[animIdx];
+ animToMeshToAnimMeshIndices[aiAnimMesh->mName.C_Str()].emplace_back(nodeMeshIdx, animIdx);
}
+ }
- for (int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
+ for (const auto& animToMeshIndex : animToMeshToAnimMeshIndices)
+ {
+ AZStd::shared_ptr blendShapeData =
+ AZStd::make_shared();
+
+ // Some DCC tools, like Maya, include a full path separated by '.' in the node names.
+ // For example, "cone_skin_blendShapeNode.cone_squash"
+ // Downstream processing doesn't want anything but the last part of that node name,
+ // so find the last '.' and remove anything before it.
+ AZStd::string nodeName(animToMeshIndex.first);
+ size_t dotIndex = nodeName.rfind('.');
+ if (dotIndex != AZStd::string::npos)
{
- AZStd::shared_ptr blendShapeData =
- AZStd::make_shared();
-
- aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[animIdx];
- AZStd::string nodeName(aiAnimMesh->mName.C_Str());
- size_t dotIndex = nodeName.rfind('.');
- if (dotIndex != AZStd::string::npos)
- {
- nodeName.erase(0, dotIndex + 1);
- }
- RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "BlendShape");
- AZ_TraceContext("Blend shape name", nodeName);
+ nodeName.erase(0, dotIndex + 1);
+ }
+ int vertexOffset = 0;
+ RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "BlendShape");
+ AZ_TraceContext("Blend shape name", nodeName);
+ for (const auto& meshIndex : animToMeshIndex.second)
+ {
+ int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[meshIndex.first];
+ const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx];
+ const aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[meshIndex.second];
AZStd::bitset uvSetUsedFlags;
for (AZ::u8 uvSetIndex = 0; uvSetIndex < SceneData::GraphData::BlendShapeData::MaxNumUVSets; ++uvSetIndex)
@@ -128,7 +142,7 @@ namespace AZ
context.m_sourceSceneSystem.ConvertUnit(vertex);
blendShapeData->AddPosition(vertex);
- blendShapeData->SetVertexIndexToControlPointIndexMap(vertIdx, vertIdx);
+ blendShapeData->SetVertexIndexToControlPointIndexMap(vertIdx + vertexOffset, vertIdx + vertexOffset);
// Add normals
if (aiAnimMesh->HasNormals())
@@ -191,33 +205,36 @@ namespace AZ
}
for (int idx = 0; idx < face.mNumIndices; ++idx)
{
- blendFace.vertexIndex[idx] = face.mIndices[idx];
+ blendFace.vertexIndex[idx] = face.mIndices[idx] + vertexOffset;
}
blendShapeData->AddFace(blendFace);
}
+ vertexOffset += aiMesh->mNumVertices;
- // Report problem if no vertex or face converted to MeshData
- if (blendShapeData->GetVertexCount() <= 0 || blendShapeData->GetFaceCount() <= 0)
- {
- AZ_Error(Utilities::ErrorWindow, false, "Missing geometry data in blendshape node %s.", nodeName.c_str());
- return Events::ProcessingResult::Failure;
- }
- Containers::SceneGraph::NodeIndex newIndex =
- context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
+ }
- Events::ProcessingResult blendShapeResult;
- AssImpSceneAttributeDataPopulatedContext dataPopulated(context, blendShapeData, newIndex, nodeName);
- blendShapeResult = Events::Process(dataPopulated);
- if (blendShapeResult != Events::ProcessingResult::Failure)
- {
- blendShapeResult = AddAttributeDataNodeWithContexts(dataPopulated);
- }
- combinedBlendShapeResult += blendShapeResult;
+ // Report problem if no vertex or face converted to MeshData
+ if (blendShapeData->GetVertexCount() <= 0 || blendShapeData->GetFaceCount() <= 0)
+ {
+ AZ_Error(Utilities::ErrorWindow, false, "Missing geometry data in blendshape node %s.", nodeName.c_str());
+ return Events::ProcessingResult::Failure;
}
+ Containers::SceneGraph::NodeIndex newIndex =
+ context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
+
+ Events::ProcessingResult blendShapeResult;
+ AssImpSceneAttributeDataPopulatedContext dataPopulated(context, blendShapeData, newIndex, nodeName);
+ blendShapeResult = Events::Process(dataPopulated);
+
+ if (blendShapeResult != Events::ProcessingResult::Failure)
+ {
+ blendShapeResult = AddAttributeDataNodeWithContexts(dataPopulated);
+ }
+ combinedBlendShapeResult += blendShapeResult;
}
return combinedBlendShapeResult.GetResult();
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp
index 4467d6933b..5b43941715 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp
@@ -46,8 +46,8 @@ namespace AZ
}
void EnumBonesInNode(
- const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList,
- AZStd::unordered_map& boneLookup)
+ const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList,
+ AZStd::unordered_map& boneLookup)
{
/* From AssImp Documentation
a) Create a map or a similar container to store which nodes are necessary for the skeleton. Pre-initialise it for all nodes with a "no".
@@ -62,14 +62,14 @@ namespace AZ
for (unsigned meshIndex = 0; meshIndex < node->mNumMeshes; ++meshIndex)
{
- aiMesh* mesh = scene->mMeshes[node->mMeshes[meshIndex]];
+ const aiMesh* mesh = scene->mMeshes[node->mMeshes[meshIndex]];
for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
{
- aiBone* bone = mesh->mBones[boneIndex];
+ const aiBone* bone = mesh->mBones[boneIndex];
- aiNode* boneNode = scene->mRootNode->FindNode(bone->mName);
- aiNode* boneParent = boneNode->mParent;
+ const aiNode* boneNode = scene->mRootNode->FindNode(bone->mName);
+ const aiNode* boneParent = boneNode->mParent;
mainBoneList[bone->mName.C_Str()] = boneNode;
boneLookup[bone->mName.C_Str()] = bone;
@@ -85,8 +85,8 @@ namespace AZ
}
void EnumChildren(
- const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList,
- AZStd::unordered_map& boneLookup)
+ const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList,
+ AZStd::unordered_map& boneLookup)
{
EnumBonesInNode(scene, node, mainBoneList, boneLookup);
@@ -102,7 +102,7 @@ namespace AZ
{
AZ_TraceContext("Importer", "Bone");
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if (IsPivotNode(currentNode->mName))
@@ -118,8 +118,8 @@ namespace AZ
}
else
{
- AZStd::unordered_map mainBoneList;
- AZStd::unordered_map boneLookup;
+ AZStd::unordered_map mainBoneList;
+ AZStd::unordered_map boneLookup;
EnumChildren(scene, scene->mRootNode, mainBoneList, boneLookup);
if (mainBoneList.find(currentNode->mName.C_Str()) != mainBoneList.end())
@@ -172,7 +172,7 @@ namespace AZ
}
aiMatrix4x4 transform = currentNode->mTransformation;
- aiNode* parent = currentNode->mParent;
+ const aiNode* parent = currentNode->mParent;
while (parent)
{
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp
index 7ebdb55363..75fa39105f 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp
@@ -11,6 +11,7 @@
*/
#include
+#include
#include
#include
#include
@@ -44,7 +45,7 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast(context);
if (serializeContext)
{
- serializeContext->Class()->Version(2); // LYN-2576
+ serializeContext->Class()->Version(3); // LYN-3250
}
}
@@ -55,43 +56,64 @@ namespace AZ
{
return Events::ProcessingResult::Ignored;
}
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
- GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context));
- if (!meshDataResult.IsSuccess())
+ // This node has at least one mesh, verify that the color channel counts are the same for all meshes.
+ const int expectedColorChannels = scene->mMeshes[currentNode->mMeshes[0]]->GetNumColorChannels();
+ const bool allMeshesHaveSameNumberOfColorChannels =
+ AZStd::all_of(currentNode->mMeshes + 1, currentNode->mMeshes + currentNode->mNumMeshes, [scene, expectedColorChannels](const unsigned int meshIndex)
+ {
+ return scene->mMeshes[meshIndex]->GetNumColorChannels() == expectedColorChannels;
+ });
+
+ AZ_Error(
+ Utilities::ErrorWindow,
+ allMeshesHaveSameNumberOfColorChannels,
+ "Color channel counts for node %s has meshes with different color channel counts. "
+ "The color channel count for the first mesh will be used, and placeholder incorrect color values "
+ "will be generated to allow the data to process, but the source art needs to be fixed to correct this. "
+ "All meshes on this node should have the same number of color channels.",
+ currentNode->mName.C_Str());
+
+ if (expectedColorChannels == 0)
{
- return meshDataResult.GetError();
- }
- const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue());
-
- size_t vertexCount = parentMeshData->GetVertexCount();
-
- int sdkMeshIndex = parentMeshData->GetSdkMeshIndex();
- if (sdkMeshIndex < 0)
- {
- AZ_Error(Utilities::ErrorWindow, false,
- "Tried to construct color stream attribute for invalid or non-mesh parent data, mesh index is missing");
- return Events::ProcessingResult::Failure;
+ return Events::ProcessingResult::Ignored;
}
- aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
+ const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
Events::ProcessingResultCombiner combinedVertexColorResults;
- for (int colorSetIndex = 0; colorSetIndex < mesh->GetNumColorChannels(); ++colorSetIndex)
+ for (int colorSetIndex = 0; colorSetIndex < expectedColorChannels; ++colorSetIndex)
{
+
AZStd::shared_ptr vertexColors =
AZStd::make_shared();
vertexColors->ReserveContainerSpace(vertexCount);
- for (int v = 0; v < mesh->mNumVertices; ++v)
+ for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
- AZ::SceneAPI::DataTypes::Color vertexColor(
- AssImpSDKWrapper::AssImpTypeConverter::ToColor(mesh->mColors[colorSetIndex][v]));
- vertexColors->AppendColor(vertexColor);
+ const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
+ for (int v = 0; v < mesh->mNumVertices; ++v)
+ {
+ if (colorSetIndex < mesh->GetNumColorChannels())
+ {
+ AZ::SceneAPI::DataTypes::Color vertexColor(
+ AssImpSDKWrapper::AssImpTypeConverter::ToColor(mesh->mColors[colorSetIndex][v]));
+ vertexColors->AppendColor(vertexColor);
+ }
+ else
+ {
+ // An error was already emitted if this mesh has less color channels
+ // than other meshes on the parent node. Append an arbitrary color value, fully opaque black,
+ // so the mesh can still be processed.
+ // It's better to let the engine load a partially valid mesh than to completely fail.
+ vertexColors->AppendColor(AZ::SceneAPI::DataTypes::Color(0.0f,0.0f,0.0f,1.0f));
+ }
+ }
}
- AZStd::string nodeName(AZStd::string::format("%s%d",m_defaultNodeName,colorSetIndex));
+ AZStd::string nodeName(AZStd::string::format("%s%d", m_defaultNodeName, colorSetIndex));
Containers::SceneGraph::NodeIndex newIndex =
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
@@ -106,9 +128,7 @@ namespace AZ
combinedVertexColorResults += colorMapResults;
}
-
return combinedVertexColorResults.GetResult();
-
}
} // namespace FbxSceneBuilder
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp
index 80ac01ebdf..e79eaa09aa 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp
@@ -69,7 +69,7 @@ namespace AZ
aiMatrix4x4 GetConcatenatedLocalTransform(const aiNode* currentNode)
{
- aiNode* parent = currentNode->mParent;
+ const aiNode* parent = currentNode->mParent;
aiMatrix4x4 combinedTransform = currentNode->mTransformation;
while (parent)
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp
index e314804ea1..a912b90e34 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp
@@ -62,7 +62,7 @@ namespace AZ
for (int idx = 0; idx < context.m_sourceNode.m_assImpNode->mNumMeshes; ++idx)
{
int meshIndex = context.m_sourceNode.m_assImpNode->mMeshes[idx];
- aiMesh* assImpMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshIndex];
+ const aiMesh* assImpMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshIndex];
AZ_Assert(assImpMesh, "Asset Importer Mesh should not be null.");
int materialIndex = assImpMesh->mMaterialIndex;
AZ_TraceContext("Material Index", materialIndex);
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp
index cafb96934d..193a1f9fd5 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp
@@ -45,7 +45,7 @@ namespace AZ
{
AZ_TraceContext("Importer", "Mesh");
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if (!context.m_sourceNode.ContainsMesh() || IsSkinnedMesh(*currentNode, *scene))
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp
index 145dc9a457..f4a5fd0f93 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp
@@ -45,7 +45,7 @@ namespace AZ
{
AZ_TraceContext("Importer", "Skin");
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if (!context.m_sourceNode.ContainsMesh() || !IsSkinnedMesh(*currentNode, *scene))
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp
index abcbf10b4a..d8503857cc 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp
@@ -51,7 +51,7 @@ namespace AZ
{
AZ_TraceContext("Importer", "Skin Weights");
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if(currentNode->mNumMeshes <= 0)
@@ -59,35 +59,21 @@ namespace AZ
return Events::ProcessingResult::Ignored;
}
- GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context));
- if (!meshDataResult.IsSuccess())
- {
- return meshDataResult.GetError();
- }
- const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue());
+ Events::ProcessingResultCombiner combinedSkinWeightsResult;
- int parentMeshIndex = parentMeshData->GetSdkMeshIndex();
+ // Don't create this until a bone with weights is encountered
+ Containers::SceneGraph::NodeIndex weightsIndexForMesh;
+ AZStd::string skinWeightName;
+ AZStd::shared_ptr skinWeightData;
- Events::ProcessingResultCombiner combinedSkinWeightsResult;
+ const uint64_t totalVertices = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
+ int vertexCount = 0;
for(unsigned nodeMeshIndex = 0; nodeMeshIndex < currentNode->mNumMeshes; ++nodeMeshIndex)
{
- if (nodeMeshIndex != parentMeshIndex)
- {
- // Only generate skinning data for the parent mesh.
- // Each AssImp mesh is assigned to a unique node,
- // so the skinning data should be generated as a child node
- // for the associated parent mesh.
- continue;
- }
int sceneMeshIndex = currentNode->mMeshes[nodeMeshIndex];
const aiMesh* mesh = scene->mMeshes[sceneMeshIndex];
- // Don't create this until a bone with weights is encountered
- Containers::SceneGraph::NodeIndex weightsIndexForMesh;
- AZStd::string skinWeightName;
- AZStd::shared_ptr skinWeightData;
-
for(unsigned b = 0; b < mesh->mNumBones; ++b)
{
const aiBone* bone = mesh->mBones[b];
@@ -100,7 +86,6 @@ namespace AZ
if (!weightsIndexForMesh.IsValid())
{
skinWeightName = s_skinWeightName;
- skinWeightName += AZStd::to_string(nodeMeshIndex);
RenamedNodesMap::SanitizeNodeName(skinWeightName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
weightsIndexForMesh =
@@ -116,23 +101,25 @@ namespace AZ
}
Pending pending;
pending.m_bone = bone;
- pending.m_numVertices = mesh->mNumVertices;
+ pending.m_numVertices = totalVertices;
pending.m_skinWeightData = skinWeightData;
+ pending.m_vertOffset = vertexCount;
m_pendingSkinWeights.push_back(pending);
}
+ vertexCount += mesh->mNumVertices;
+ }
- Events::ProcessingResult skinWeightsResult;
- AssImpSceneAttributeDataPopulatedContext dataPopulated(context, skinWeightData, weightsIndexForMesh, skinWeightName);
- skinWeightsResult = Events::Process(dataPopulated);
-
- if (skinWeightsResult != Events::ProcessingResult::Failure)
- {
- skinWeightsResult = AddAttributeDataNodeWithContexts(dataPopulated);
- }
+ Events::ProcessingResult skinWeightsResult;
+ AssImpSceneAttributeDataPopulatedContext dataPopulated(context, skinWeightData, weightsIndexForMesh, skinWeightName);
+ skinWeightsResult = Events::Process(dataPopulated);
- combinedSkinWeightsResult += skinWeightsResult;
+ if (skinWeightsResult != Events::ProcessingResult::Failure)
+ {
+ skinWeightsResult = AddAttributeDataNodeWithContexts(dataPopulated);
}
+ combinedSkinWeightsResult += skinWeightsResult;
+
return combinedSkinWeightsResult.GetResult();
}
@@ -153,7 +140,7 @@ namespace AZ
link.boneId = boneId;
link.weight = it.m_bone->mWeights[weight].mWeight;
- it.m_skinWeightData->AddAndSortLink(it.m_bone->mWeights[weight].mVertexId, link);
+ it.m_skinWeightData->AddAndSortLink(it.m_bone->mWeights[weight].mVertexId + it.m_vertOffset, link);
}
}
const auto result = m_pendingSkinWeights.empty() ? Events::ProcessingResult::Ignored : Events::ProcessingResult::Success;
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h
index f048fe0af2..655c838701 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h
@@ -61,6 +61,7 @@ namespace AZ
{
const aiBone* m_bone = nullptr;
unsigned m_numVertices = 0;
+ unsigned m_vertOffset = 0;
AZStd::shared_ptr m_skinWeightData;
};
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp
index 992a6a6ab1..47b7e410b4 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp
@@ -11,6 +11,7 @@
*/
#include
+#include
#include
#include
#include
@@ -44,7 +45,7 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast(context);
if (serializeContext)
{
- serializeContext->Class()->Version(2); // LYN-2576
+ serializeContext->Class()->Version(3); // LYN-3250
}
}
@@ -55,62 +56,79 @@ namespace AZ
{
return Events::ProcessingResult::Ignored;
}
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
-
- GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context));
- if (!meshDataResult.IsSuccess())
+
+ const auto meshHasTangentsAndBitangents = [&scene](const unsigned int meshIndex)
{
- return meshDataResult.GetError();
- }
- const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue());
-
- size_t vertexCount = parentMeshData->GetVertexCount();
+ return scene->mMeshes[meshIndex]->HasTangentsAndBitangents();
+ };
- int sdkMeshIndex = parentMeshData->GetSdkMeshIndex();
- if (sdkMeshIndex < 0 || sdkMeshIndex >= currentNode->mNumMeshes)
+ // If there are no tangents on any meshes, there's nothing to import in this function.
+ const bool anyMeshHasTangentsAndBitangents = AZStd::any_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
+ if (!anyMeshHasTangentsAndBitangents)
{
- AZ_Error(Utilities::ErrorWindow, false,
- "Tried to construct tangent stream attribute for invalid or non-mesh parent data, mesh index is invalid");
- return Events::ProcessingResult::Failure;
+ return Events::ProcessingResult::Ignored;
}
- aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
-
- if (!mesh->HasTangentsAndBitangents())
+ // AssImp nodes with multiple meshes on them occur when AssImp split a mesh on material.
+ // This logic recombines those meshes to minimize the changes needed to replace FBX SDK with AssImp, FBX SDK did not separate meshes,
+ // and the engine has code to do this later.
+ const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents);
+ if (!allMeshesHaveTangentsAndBitangents)
{
- return Events::ProcessingResult::Ignored;
+ const char* mixedTangentsError =
+ "Node with name %s has meshes with and without tangents. "
+ "Placeholder incorrect tangents will be generated to allow the data to process, "
+ "but the source art needs to be fixed to correct this. Either apply tangents to all meshes on this node, "
+ "or remove all tangents from all meshes on this node.";
+ AZ_Error(
+ Utilities::ErrorWindow, false, mixedTangentsError, currentNode->mName.C_Str());
}
+ const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
+
AZStd::shared_ptr tangentStream =
AZStd::make_shared();
-
// AssImp only has one tangentStream per mesh.
tangentStream->SetTangentSetIndex(0);
tangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
tangentStream->ReserveContainerSpace(vertexCount);
-
- for (int v = 0; v < mesh->mNumVertices; ++v)
+ for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
- // Vector4's constructor that takes in a vector3 sets w to 1.0f automatically.
- const Vector4 tangent(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mTangents[v]));
- tangentStream->AppendTangent(tangent);
+ const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
+
+ for (int v = 0; v < mesh->mNumVertices; ++v)
+ {
+ if (!mesh->HasTangentsAndBitangents())
+ {
+ // This node has mixed meshes with and without tangents.
+ // An error was already thrown above. Output stub tangents so
+ // the mesh can still be output in some form, even if the data isn't correct.
+ // The tangent count needs to match the vertex count on the associated mesh node.
+ tangentStream->AppendTangent(Vector4(0.f, 1.f, 0.f, 1.f));
+ }
+ else
+ {
+ const Vector4 tangent(
+ AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mTangents[v]));
+ tangentStream->AppendTangent(tangent);
+ }
+ }
}
- AZStd::string nodeName(AZStd::string::format("%s", m_defaultNodeName));
Containers::SceneGraph::NodeIndex newIndex =
- context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str());
+ context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName);
Events::ProcessingResult tangentResults;
- AssImpSceneAttributeDataPopulatedContext dataPopulated(context, tangentStream, newIndex, nodeName.c_str());
+ AssImpSceneAttributeDataPopulatedContext dataPopulated(context, tangentStream, newIndex, m_defaultNodeName);
tangentResults = Events::Process(dataPopulated);
if (tangentResults != Events::ProcessingResult::Failure)
{
tangentResults = AddAttributeDataNodeWithContexts(dataPopulated);
}
-
return tangentResults;
}
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp
index 84c0e3e18c..5357c32fa9 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp
@@ -50,7 +50,7 @@ namespace AZ
Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context)
{
AZ_TraceContext("Importer", "transform");
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName))
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp
index f5f47b233d..e37a4f4285 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp
@@ -12,17 +12,19 @@
#include
#include
+#include
+#include
#include
#include
#include
#include
#include
#include
-#include
-#include
+#include
#include
#include
-#include
+#include
+#include
#include
#include
@@ -45,7 +47,7 @@ namespace AZ
SerializeContext* serializeContext = azrtti_cast(context);
if (serializeContext)
{
- serializeContext->Class()->Version(3); // LYN-2506
+ serializeContext->Class()->Version(4); // LYN-3250
}
}
@@ -56,28 +58,53 @@ namespace AZ
{
return Events::ProcessingResult::Ignored;
}
- aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
+ const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
- GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context));
- if (!meshDataResult.IsSuccess())
+ // AssImp separates meshes that have multiple materials.
+ // This code re-combines them to match previous FBX SDK behavior,
+ // so they can be separated by engine code instead.
+ bool foundTextureCoordinates = false;
+ AZStd::array meshesPerTextureCoordinateIndex = {};
+ for (int localMeshIndex = 0; localMeshIndex < currentNode->mNumMeshes; ++localMeshIndex)
{
- return meshDataResult.GetError();
+ aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[localMeshIndex]];
+ for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
+ {
+ if (!mesh->mTextureCoords[texCoordIndex])
+ {
+ continue;
+ }
+ ++meshesPerTextureCoordinateIndex[texCoordIndex];
+ foundTextureCoordinates = true;
+ }
}
- const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue());
- size_t vertexCount = parentMeshData->GetVertexCount();
+ if (!foundTextureCoordinates)
+ {
+ return Events::ProcessingResult::Ignored;
+ }
- int sdkMeshIndex = parentMeshData->GetSdkMeshIndex();
- AZ_Assert(sdkMeshIndex >= 0,
- "Tried to construct uv stream attribute for invalid or non-mesh parent data, mesh index is missing");
+ const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
- aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
+ for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
+ {
+ int meshesWithIndex = meshesPerTextureCoordinateIndex[texCoordIndex];
+ AZ_Error(
+ Utilities::ErrorWindow,
+ meshesWithIndex == 0 || meshesWithIndex == currentNode->mNumMeshes,
+ "Texture coordinate index %d for node %s is not on all meshes on this node. "
+ "Placeholder arbitrary texture values will be generated to allow the data to process, but the source art "
+ "needs to be fixed to correct this. All meshes on this node should have the same number of texture coordinate channels.",
+ texCoordIndex,
+ currentNode->mName.C_Str());
+ }
Events::ProcessingResultCombiner combinedUvMapResults;
- for (int texCoordIndex = 0; texCoordIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++texCoordIndex)
+ for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
{
- if (!mesh->mTextureCoords[texCoordIndex])
+ // No meshes have this texture coordinate index, skip it.
+ if (meshesPerTextureCoordinateIndex[texCoordIndex] == 0)
{
continue;
}
@@ -85,24 +112,55 @@ namespace AZ
AZStd::shared_ptr uvMap =
AZStd::make_shared();
uvMap->ReserveContainerSpace(vertexCount);
-
+ bool customNameFound = false;
AZStd::string name(AZStd::string::format("%s%d", m_defaultNodeName, texCoordIndex));
- if (mesh->mTextureCoordsNames[texCoordIndex].length)
+ for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
- name = mesh->mTextureCoordsNames[texCoordIndex].C_Str();
+ const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
+ if(mesh->mTextureCoords[texCoordIndex])
+ {
+ if (mesh->mTextureCoordsNames[texCoordIndex].length > 0)
+ {
+ if (!customNameFound)
+ {
+ name = mesh->mTextureCoordsNames[texCoordIndex].C_Str();
+ customNameFound = true;
+ }
+ else
+ {
+ AZ_Warning(Utilities::WarningWindow,
+ strcmp(name.c_str(), mesh->mTextureCoordsNames[texCoordIndex].C_Str()) == 0,
+ "Node %s has conflicting mesh coordinate names at index %d, %s and %s. Using %s.",
+ currentNode->mName.C_Str(),
+ texCoordIndex,
+ name.c_str(),
+ mesh->mTextureCoordsNames[texCoordIndex].C_Str(),
+ name.c_str());
+ }
+ }
+ }
+
+ for (int v = 0; v < mesh->mNumVertices; ++v)
+ {
+ if (mesh->mTextureCoords[texCoordIndex])
+ {
+ AZ::Vector2 vertexUV(
+ mesh->mTextureCoords[texCoordIndex][v].x,
+ // The engine's V coordinate is reverse of how it's stored in the FBX file.
+ 1.0f - mesh->mTextureCoords[texCoordIndex][v].y);
+ uvMap->AppendUV(vertexUV);
+ }
+ else
+ {
+ // An error was already emitted if the UV channels for all meshes on this node do not match.
+ // Append an arbitrary UV value so that the mesh can still be processed.
+ // It's better to let the engine load a partially valid mesh than to completely fail.
+ uvMap->AppendUV(AZ::Vector2::CreateZero());
+ }
+ }
}
uvMap->SetCustomName(name.c_str());
-
- for (int v = 0; v < mesh->mNumVertices; ++v)
- {
- AZ::Vector2 vertexUV(
- mesh->mTextureCoords[texCoordIndex][v].x,
- // The engine's V coordinate is reverse of how it's stored in the FBX file.
- 1.0f - mesh->mTextureCoords[texCoordIndex][v].y);
- uvMap->AppendUV(vertexUV);
- }
-
Containers::SceneGraph::NodeIndex newIndex =
context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, name.c_str());
@@ -116,6 +174,7 @@ namespace AZ
}
combinedUvMapResults += uvMapResults;
+
}
return combinedUvMapResults.GetResult();
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp
index 59821336e0..c90fe7d1f3 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -24,7 +25,7 @@
namespace AZ::SceneAPI::FbxSceneBuilder
{
- bool BuildSceneMeshFromAssImpMesh(aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes,
+ bool BuildSceneMeshFromAssImpMesh(const aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes,
const AZStd::function()>& makeMeshFunc)
{
AZStd::unordered_map assImpMatIndexToLYIndex;
@@ -34,17 +35,18 @@ namespace AZ::SceneAPI::FbxSceneBuilder
{
return false;
}
+ auto newMesh = makeMeshFunc();
+ newMesh->SetUnitSizeInMeters(sceneSystem.GetUnitSizeInMeters());
+ newMesh->SetOriginalUnitSizeInMeters(sceneSystem.GetOriginalUnitSizeInMeters());
+
+ // AssImp separates meshes that have multiple materials.
+ // This code re-combines them to match previous FBX SDK behavior,
+ // so they can be separated by engine code instead.
+ int vertOffset = 0;
for (int m = 0; m < currentNode->mNumMeshes; ++m)
{
- auto newMesh = makeMeshFunc();
-
- newMesh->SetUnitSizeInMeters(sceneSystem.GetUnitSizeInMeters());
- newMesh->SetOriginalUnitSizeInMeters(sceneSystem.GetOriginalUnitSizeInMeters());
-
- newMesh->SetSdkMeshIndex(m);
-
- aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[m]];
+ const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[m]];
// Lumberyard materials are created in order based on mesh references in the scene
if (assImpMatIndexToLYIndex.find(mesh->mMaterialIndex) == assImpMatIndexToLYIndex.end())
@@ -59,7 +61,7 @@ namespace AZ::SceneAPI::FbxSceneBuilder
sceneSystem.SwapVec3ForUpAxis(vertex);
sceneSystem.ConvertUnit(vertex);
newMesh->AddPosition(vertex);
- newMesh->SetVertexIndexToControlPointIndexMap(vertIdx, vertIdx);
+ newMesh->SetVertexIndexToControlPointIndexMap(vertIdx + vertOffset, vertIdx + vertOffset);
if (mesh->HasNormals())
{
@@ -86,14 +88,15 @@ namespace AZ::SceneAPI::FbxSceneBuilder
}
for (int idx = 0; idx < face.mNumIndices; ++idx)
{
- meshFace.vertexIndex[idx] = face.mIndices[idx];
+ meshFace.vertexIndex[idx] = face.mIndices[idx] + vertOffset;
}
newMesh->AddFace(meshFace, assImpMatIndexToLYIndex[mesh->mMaterialIndex]);
}
+ vertOffset += mesh->mNumVertices;
- meshes.push_back(newMesh);
}
+ meshes.push_back(newMesh);
return true;
}
@@ -127,4 +130,13 @@ namespace AZ::SceneAPI::FbxSceneBuilder
azrtti_cast(parentData);
return AZ::Success(parentMeshData);
}
+
+ uint64_t GetVertexCountForAllMeshesOnNode(const aiNode& node, const aiScene& scene)
+ {
+ return AZStd::accumulate(node.mMeshes, node.mMeshes + node.mNumMeshes, uint64_t{ 0u },
+ [&scene](auto runningTotal, unsigned int meshIndex)
+ {
+ return runningTotal + scene.mMeshes[meshIndex]->mNumVertices;
+ });
+ }
}
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h
index c0b5c044cb..3c7d3d2102 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h
@@ -44,11 +44,16 @@ namespace AZ
namespace FbxSceneBuilder
{
- bool BuildSceneMeshFromAssImpMesh(aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes,
+ bool BuildSceneMeshFromAssImpMesh(const aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes,
const AZStd::function()>& makeMeshFunc);
typedef AZ::Outcome GetMeshDataFromParentResult;
GetMeshDataFromParentResult GetMeshDataFromParent(AssImpSceneNodeAppendedContext& context);
+
+ // If a node in the original scene file has a mesh with multiple materials on it, the associated AssImp
+ // node will have multiple meshes on it, broken apart per material. This returns the total number
+ // of vertices on all meshes on the given node.
+ uint64_t GetVertexCountForAllMeshesOnNode(const aiNode& node, const aiScene& scene);
}
}
}
diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp
index abc095d583..b67696e3cc 100644
--- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp
+++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp
@@ -27,6 +27,7 @@ namespace AZ
Containers::SceneGraph::NodeIndex parentNode, const char* defaultName)
{
AZ_TraceContext("Node name", name);
+ const AZStd::string originalNodeName(name);
bool isNameUpdated = false;
// Nodes can't have an empty name, except of the root, otherwise nodes can't be referenced.
@@ -56,7 +57,7 @@ namespace AZ
// can't reference the same parent in that case. This is to make sure the node can be quickly found as
// the full path will be unique. To fix any issues, an index is appended.
size_t index = 1;
- size_t offset = name.length();
+ const size_t offset = name.length();
while (graph.Find(parentNode, name).IsValid())
{
// Remove the previously tried extension.
@@ -71,7 +72,8 @@ namespace AZ
if (isNameUpdated)
{
AZ_TraceContext("New node name", name);
- AZ_TracePrintf(Utilities::WarningWindow, "The name of the node was invalid or conflicting and was updated.");
+ AZ_TracePrintf(Utilities::WarningWindow, "The name of the node '%s' was invalid or conflicting and was updated to '%s'.",
+ originalNodeName.c_str(), name.c_str());
}
return isNameUpdated;
diff --git a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp
index eccbe729c6..2cda1e68ae 100644
--- a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp
+++ b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp
@@ -38,12 +38,14 @@ namespace AZ
{
AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "AssImpSceneWrapper::LoadSceneFromFile %s", fileName);
AZ_TraceContext("Filename", fileName);
+ // aiProcess_JoinIdenticalVertices is not enabled because O3DE has a mesh optimizer that also does this,
+ // this flag is disabled to keep AssImp output similar to FBX SDK to reduce downstream bugs for the initial AssImp release.
+ // There's currently a minimum of properties and flags set to maximize compatibility with the existing node graph.
m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, false);
m_sceneFileName = fileName;
m_assImpScene = m_importer.ReadFile(fileName,
aiProcess_Triangulate //Triangulates all faces of all meshes
- | aiProcess_JoinIdenticalVertices //Identifies and joins identical vertex data sets for the imported meshes
| aiProcess_LimitBoneWeights //Limits the number of bones that can affect a vertex to a maximum value
//dropping the least important and re-normalizing
| aiProcess_GenNormals); //Generate normals for meshes
diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp
index e99788d570..f3c01df2e3 100644
--- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp
+++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp
@@ -19,6 +19,16 @@ namespace AZ::SceneAPI::Utilities
m_output += AZStd::string::format("\t%s: %s\n", name, data);
}
+ void DebugOutput::WriteArray(const char* name, const unsigned int* data, int size)
+ {
+ m_output += AZStd::string::format("\t%s: ", name);
+ for (int index = 0; index < size; ++index)
+ {
+ m_output += AZStd::string::format("%d, ", data[index]);
+ }
+ m_output += AZStd::string::format("\n");
+ }
+
void DebugOutput::Write(const char* name, const AZStd::string& data)
{
Write(name, data.c_str());
diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h
index a598c996c9..83dc9dd6ab 100644
--- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h
+++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h
@@ -29,6 +29,7 @@ namespace AZ::SceneAPI::Utilities
void Write(const char* name, const AZStd::vector>& data);
SCENE_CORE_API void Write(const char* name, const char* data);
+ SCENE_CORE_API void WriteArray(const char* name, const unsigned int* data, int size);
SCENE_CORE_API void Write(const char* name, const AZStd::string& data);
SCENE_CORE_API void Write(const char* name, double data);
SCENE_CORE_API void Write(const char* name, uint64_t data);
diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp
index 902928d404..7d81166829 100644
--- a/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp
+++ b/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp
@@ -285,8 +285,26 @@ namespace AZ
void BlendShapeData::GetDebugOutput(SceneAPI::Utilities::DebugOutput& output) const
{
output.Write("Positions", m_positions);
+ int index = 0;
+ for (const auto& position : m_positions)
+ {
+ output.Write(AZStd::string::format("\t%d", index).c_str(), position);
+ ++index;
+ }
+ index = 0;
output.Write("Normals", m_normals);
+ for (const auto& normal : m_normals)
+ {
+ output.Write(AZStd::string::format("\t%d", index).c_str(), normal);
+ ++index;
+ }
+ index = 0;
output.Write("Faces", m_faces);
+ for (const auto& face : m_faces)
+ {
+ output.WriteArray(AZStd::string::format("\t%d", index).c_str(), face.vertexIndex, 3);
+ ++index;
+ }
}
} // GraphData
} // SceneData
diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp
index 8bf49b2898..8f464240c9 100644
--- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp
+++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp
@@ -45,7 +45,6 @@ namespace AZ
behaviorContext->Class()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "scene")
- ->Method("GetSdkMeshIndex", &MeshData::GetSdkMeshIndex)
->Method("GetControlPointIndex", &MeshData::GetControlPointIndex)
->Method("GetUsedControlPointCount", &MeshData::GetUsedControlPointCount)
->Method("GetUsedPointIndexForControlPoint", &MeshData::GetUsedPointIndexForControlPoint)
@@ -77,10 +76,6 @@ namespace AZ
void MeshData::CloneAttributesFrom(const IGraphObject* sourceObject)
{
IMeshData::CloneAttributesFrom(sourceObject);
- if (const auto* typedSource = azrtti_cast(sourceObject))
- {
- SetSdkMeshIndex(typedSource->GetSdkMeshIndex());
- }
}
void MeshData::AddPosition(const AZ::Vector3& position)
@@ -111,15 +106,6 @@ namespace AZ
m_faceMaterialIds.push_back(faceMaterialId);
}
- void MeshData::SetSdkMeshIndex(int sdkMeshIndex)
- {
- m_sdkMeshIndex = sdkMeshIndex;
- }
- int MeshData::GetSdkMeshIndex() const
- {
- return m_sdkMeshIndex;
- }
-
void MeshData::SetVertexIndexToControlPointIndexMap(int vertexIndex, int controlPointIndex)
{
m_vertexIndexToControlPointIndexMap[vertexIndex] = controlPointIndex;
@@ -206,8 +192,26 @@ namespace AZ
void MeshData::GetDebugOutput(SceneAPI::Utilities::DebugOutput& output) const
{
output.Write("Positions", m_positions);
+ int index = 0;
+ for (const auto& position : m_positions)
+ {
+ output.Write(AZStd::string::format("\t%d", index).c_str(), position);
+ ++index;
+ }
+ index = 0;
output.Write("Normals", m_normals);
+ for (const auto& normal : m_normals)
+ {
+ output.Write(AZStd::string::format("\t%d", index).c_str(), normal);
+ ++index;
+ }
+ index = 0;
output.Write("FaceList", m_faceList);
+ for (const auto& face : m_faceList)
+ {
+ output.WriteArray(AZStd::string::format("\t%d", index).c_str(), face.vertexIndex, 3);
+ ++index;
+ }
output.Write("FaceMaterialIds", m_faceMaterialIds);
}
}
diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h
index c5197d6cb4..4321096857 100644
--- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h
+++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h
@@ -49,9 +49,6 @@ namespace AZ
SCENE_DATA_API void AddFace(const AZ::SceneAPI::DataTypes::IMeshData::Face& face,
unsigned int faceMaterialId = AZ::SceneAPI::DataTypes::IMeshData::s_invalidMaterialId);
- SCENE_DATA_API void SetSdkMeshIndex(int sdkMeshIndex);
- SCENE_DATA_API int GetSdkMeshIndex() const;
-
SCENE_DATA_API void SetVertexIndexToControlPointIndexMap(int vertexIndex, int controlPointIndex);
SCENE_DATA_API size_t GetUsedControlPointCount() const override;
SCENE_DATA_API int GetControlPointIndex(int vertexIndex) const override;
@@ -80,8 +77,6 @@ namespace AZ
AZStd::unordered_map m_vertexIndexToControlPointIndexMap;
AZStd::unordered_map m_controlPointToUsedVertexIndexMap;
-
- int m_sdkMeshIndex = -1;
};
}
}
diff --git a/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp b/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp
index 79c6cfea7e..84018bbc9a 100644
--- a/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp
+++ b/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp
@@ -57,7 +57,6 @@ namespace AZ
meshData->AddNormal(Vector3{0.1f, 0.2f, 0.3f});
meshData->AddNormal(Vector3{0.4f, 0.5f, 0.6f});
meshData->SetOriginalUnitSizeInMeters(10.0f);
- meshData->SetSdkMeshIndex(1337);
meshData->SetUnitSizeInMeters(0.5f);
meshData->SetVertexIndexToControlPointIndexMap(0, 10);
meshData->SetVertexIndexToControlPointIndexMap(1, 11);
@@ -252,7 +251,6 @@ namespace AZ
ExpectExecute("TestExpectFloatEquals(meshData:GetNormal(1).z, 0.6)");
ExpectExecute("TestExpectFloatEquals(meshData:GetOriginalUnitSizeInMeters(), 10.0)");
ExpectExecute("TestExpectFloatEquals(meshData:GetUnitSizeInMeters(), 0.5)");
- ExpectExecute("TestExpectIntegerEquals(meshData:GetSdkMeshIndex(), 1337)");
ExpectExecute("TestExpectIntegerEquals(meshData:GetUsedControlPointCount(), 4)");
ExpectExecute("TestExpectIntegerEquals(meshData:GetControlPointIndex(0), 10)");
ExpectExecute("TestExpectIntegerEquals(meshData:GetControlPointIndex(1), 11)");
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslBuilder.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslBuilder.cpp
index 0ce7109067..668d6866d3 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslBuilder.cpp
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslBuilder.cpp
@@ -185,7 +185,8 @@ namespace AZ
// we can't use a temporary folder because CreateJobs API does not warrant side effects, and does not prepare a temp folder.
// we can't use the OS temp folder anyway, because many includes (eg #include "../RPI/Shadow.h") are relative and will only work from the original location
AZStd::string prependedPath = ShaderBuilderUtility::DumpAzslPrependedCode(
- BuilderName, prependedAzslSourceCode, originalLocation, ShaderBuilderUtility::ExtractStemName(fullPath.c_str()), shaderPlatformInterface->GetAPIName().GetStringView());
+ BuilderName, prependedAzslSourceCode, originalLocation, ShaderBuilderUtility::ExtractStemName(fullPath.c_str()),
+ shaderPlatformInterface->GetAPIName().GetStringView());
// run mcpp
PreprocessorData preprocessorData = PreprocessSource(prependedPath, fullPath, buildOptions.m_preprocessorSettings);
jobDescriptor.m_jobParameters[(u32)JobParameterIndices::PreprocessorError] = preprocessorData.diagnostics; // save for ProcessJob
@@ -221,7 +222,7 @@ namespace AZ
}
// eg: ("D:/p/x.a", "D:/p/x.b") -> yes
- static bool HasSameStemName(const AZStd::string& lhsPath, const AZStd::string& rhsPath)
+ static bool HasSameFileName(const AZStd::string& lhsPath, const AZStd::string& rhsPath)
{
using namespace StringFunc::Path;
AZStd::string stem1;
@@ -307,7 +308,8 @@ namespace AZ
buildOptions.m_compilerArguments.Merge(shaderAssetSource.m_compiler);
// Earlier, we declared a job dependency on the .azsl's job, let's access the produced assets:
- uint32_t subId = ShaderBuilderUtility::MakeAzslBuildProductSubId(RPI::ShaderAssetSubId::GeneratedSource, platformInterface->GetAPIType());
+ uint32_t subId = ShaderBuilderUtility::MakeAzslBuildProductSubId(
+ RPI::ShaderAssetSubId::GeneratedHlslSource, platformInterface->GetAPIType());
auto assetIdOutcome = RPI::AssetUtils::MakeAssetId(inputFiles->m_azslSourceFullPath, subId);
AZ_Warning(BuilderName, assetIdOutcome.IsSuccess(), "Product of dependency %s not found: this is an oddity but build can continue.", inputFiles->m_azslSourceFullPath.c_str());
if (assetIdOutcome.IsSuccess())
@@ -325,7 +327,7 @@ namespace AZ
AZ_TracePrintf(BuilderName, "Product output already built by %s is not reusable because of incompatible azslc CompilerHints: launching independent build", inputFiles->m_azslSourceFullPath.c_str());
}
- if (HasSameStemName(fullSourcePath, inputFiles->m_azslSourceFullPath))
+ if (HasSameFileName(fullSourcePath, inputFiles->m_azslSourceFullPath))
{
// let's add a "distinguisher" to the names of the outproduct artifacts of this build round.*
// Because otherwise the asset processor is not going to accept an overwrite of the ones output by the .azsl job
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp
index 417a5b4a88..e3b482742b 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.cpp
@@ -26,6 +26,7 @@
#include
#include
+#include // [GFX TODO] Remove when [ATOM-15472]
#include
#include
@@ -122,7 +123,7 @@ namespace AZ
namespace SubProducts = ShaderBuilderUtility::AzslSubProducts;
- Outcome AzslCompiler::EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile /* = ""*/) const
+ Outcome AzslCompiler::EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile /* = ""*/, const char * addSuffix) const
{
bool success = Compile("--full " + parameters, outputFile);
if (!success)
@@ -133,11 +134,22 @@ namespace AZ
SubProducts::Paths productPaths = SubProducts::Paths(SubProducts::Paths::capacity());
for (auto subProduct : SubProducts::SuffixListMembers)
{
- productPaths[subProduct.m_value] = outputFile.empty() ? m_inputFilePath : outputFile; // that's a reproduction of azslc's behavior (no "-o" = input name is used)
- AzFramework::StringFunc::Path::ReplaceExtension(productPaths[subProduct.m_value], subProduct.m_string.data());
+ AZStd::string subProductFilePath = outputFile.empty() ? m_inputFilePath : outputFile; // that's a reproduction of azslc's behavior (no "-o" = input name is used)
+ AzFramework::StringFunc::Path::ReplaceExtension(subProductFilePath, subProduct.m_string.data());
// append .json if it's one of those subs:
auto listOfJsons = { SubProducts::ia, SubProducts::om, SubProducts::srg, SubProducts::options, SubProducts::bindingdep };
- productPaths[subProduct.m_value] += AZStd::any_of(AZ_BEGIN_END(listOfJsons), [&](auto v) { return v == subProduct.m_value; }) ? ".json" : "";
+ subProductFilePath += AZStd::any_of(AZ_BEGIN_END(listOfJsons), [&](auto v) { return v == subProduct.m_value; }) ? ".json" : "";
+
+ // [GFX TODO] Remove when [ATOM-15472]
+ if (addSuffix)
+ {
+ // Rename the product file.
+ AZStd::string finalSubProductFilePath = AZStd::string::format("%s%s", subProductFilePath.c_str(), addSuffix);
+ AZ::IO::Move(subProductFilePath.c_str(), finalSubProductFilePath.c_str());
+ subProductFilePath = finalSubProductFilePath;
+ }
+
+ productPaths[subProduct.m_value] = subProductFilePath;
}
productPaths[SubProducts::azslin] = GetInputFilePath(); // post-fixup this one after the loop, because it's not an output of azslc, it's an output of the builder though.
return { productPaths };
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.h b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.h
index cb30da068f..8da03cd6e5 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.h
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslCompiler.h
@@ -38,8 +38,9 @@ namespace AZ
//! @param inputFilePath The target input file to compile. Should be a valid AZSL file with no preprocessing directives.
AzslCompiler(const AZStd::string& inputFilePath);
+ //! [GFX TODO] Remove @addSuffix when [ATOM-15472]
//! compile with --full and generate all .json files
- Outcome EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile = "") const;
+ Outcome EmitFullData(const AZStd::string& parameters, const AZStd::string& outputFile = "", const char * addSuffix = nullptr) const;
//! compile to HLSL independently
bool EmitShader(AZ::IO::GenericStream& outputStream, const AZStd::string& extraCompilerParams) const;
//! compile with --ia independently and populate document @output
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslData.h b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslData.h
index 4ea7a51fb1..303e2f355f 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslData.h
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslData.h
@@ -83,23 +83,41 @@ namespace AZ
AZStd::string m_azslFileName; //!< Name for the source .azsl file
};
- struct AzslCodeTopData
+
+ //! DEPRECATED [ATOM-15472]
+ //! This class is used to collect all the json files produced by the compilation
+ //! of an AZSL file as objects.
+ struct AzslData
{
+ AzslData(const AZStd::shared_ptr& a_sources) : m_sources(a_sources) { }
+
+ AZStd::shared_ptr m_sources;
+ AZStd::string m_preprocessedFullPath; // Full path to a preprocessed version of the original AZSL file
+ AZStd::string m_shaderCodePrefix; // AssetProcessor generated shader code which is added to the
+ // AZSLc emitted code prior to invoking the native shader compiler
+
SrgDataContainer m_srgData;
- AzslFunctions m_functions;
- StructContainer m_structs;
+ AzslFunctions m_functions;
+ StructContainer m_structs;
RootConstantData m_rootConstantData;
};
- struct AzslData
+ //! This class is used to collect all the json files produced by the compilation
+ //! of an AZSL file as objects.
+ struct AzslData2
{
- AzslData(const AZStd::shared_ptr& a_sources) : m_sources(a_sources) { }
+ AzslData2(const AZStd::shared_ptr& a_sources)
+ : m_sources(a_sources)
+ {
+ }
AZStd::shared_ptr m_sources;
- AZStd::string m_preprocessedFullPath; // Full path to a preprocessed version of the original AZSL file
- AZStd::string m_shaderCodePrefix; // AssetProcessor generated shader code which is added to the
- // AZSLc emitted code prior to invoking the native shader compiler
- AzslCodeTopData m_topData;
+ AZStd::string m_preprocessedFullPath; // Full path to a preprocessed version of the original AZSL file
+
+ SrgDataContainer m_srgData;
+ AzslFunctions m_functions;
+ StructContainer m_structs;
+ RootConstantData m_rootConstantData;
};
} // ShaderBuilder
} // AZ
diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
index a0f9f7db22..fb4d370621 100644
--- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
+++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp
@@ -86,7 +86,7 @@ namespace AZ
// Register AZSL's compilation products Builder
AssetBuilderSDK::AssetBuilderDesc azslBuilderDescriptor;
azslBuilderDescriptor.m_name = "AZSL Builder";
- azslBuilderDescriptor.m_version = 7; // LKG Merge
+ azslBuilderDescriptor.m_version = 8; // ATOM-15276
// register all extensions thay may carry azsl code. header. main shader. or SRG
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
azslBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsl", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
@@ -102,7 +102,7 @@ namespace AZ
// Register Shader Resource Group Layout Builder
AssetBuilderSDK::AssetBuilderDesc srgLayoutBuilderDescriptor;
srgLayoutBuilderDescriptor.m_name = "Shader Resource Group Layout Builder";
- srgLayoutBuilderDescriptor.m_version = 54; // Enable Null Rhi for AutomatedTesting
+ srgLayoutBuilderDescriptor.m_version = 55; // ATOM-15276
srgLayoutBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsl", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
srgLayoutBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.azsli", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
@@ -118,7 +118,7 @@ namespace AZ
// Register Shader Asset Builder
AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilderDescriptor;
shaderAssetBuilderDescriptor.m_name = "Shader Asset Builder";
- shaderAssetBuilderDescriptor.m_version = 98; // Enable Null Rhi for AutomatedTesting
+ shaderAssetBuilderDescriptor.m_version = 99; // ATOM-15276
// .shader file changes trigger rebuilds
shaderAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern( AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
shaderAssetBuilderDescriptor.m_busId = azrtti_typeid();
@@ -133,7 +133,7 @@ namespace AZ
shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder";
// Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
// ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
- shaderVariantAssetBuilderDescriptor.m_version = 19; // Enable Null Rhi for AutomatedTesting
+ shaderVariantAssetBuilderDescriptor.m_version = 20; // ATOM-15276
shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid();
shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
@@ -145,7 +145,7 @@ namespace AZ
// Register Precompiled Shader Builder
AssetBuilderSDK::AssetBuilderDesc precompiledShaderBuilderDescriptor;
precompiledShaderBuilderDescriptor.m_name = "Precompiled Shader Builder";
- precompiledShaderBuilderDescriptor.m_version = 7; // ATOM-14780
+ precompiledShaderBuilderDescriptor.m_version = 8; // ATOM-15276
precompiledShaderBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", AZ::PrecompiledShaderBuilder::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
precompiledShaderBuilderDescriptor.m_busId = azrtti_typeid();
precompiledShaderBuilderDescriptor.m_createJobFunction = AZStd::bind(&PrecompiledShaderBuilder::CreateJobs, &m_precompiledShaderBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
@@ -153,6 +153,43 @@ namespace AZ
m_precompiledShaderBuilder.BusConnect(precompiledShaderBuilderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, precompiledShaderBuilderDescriptor);
+
+ // Register Shader Asset Builder 2
+ AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilder2Descriptor;
+ shaderAssetBuilder2Descriptor.m_name = "Shader Asset Builder 2";
+ shaderAssetBuilder2Descriptor.m_version = 1; // ATOM-15276
+ // .shader2 file changes trigger rebuilds
+ shaderAssetBuilder2Descriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(
+ AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension2),
+ AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
+ shaderAssetBuilder2Descriptor.m_busId = azrtti_typeid();
+ shaderAssetBuilder2Descriptor.m_createJobFunction =
+ AZStd::bind(&ShaderAssetBuilder2::CreateJobs, &m_shaderAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
+ shaderAssetBuilder2Descriptor.m_processJobFunction =
+ AZStd::bind(&ShaderAssetBuilder2::ProcessJob, &m_shaderAssetBuilder2, AZStd::placeholders::_1, AZStd::placeholders::_2);
+
+ m_shaderAssetBuilder2.BusConnect(shaderAssetBuilder2Descriptor.m_busId);
+ AssetBuilderSDK::AssetBuilderBus::Broadcast(
+ &AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, shaderAssetBuilder2Descriptor);
+
+ // Register Shader Variant Asset Builder 2
+ AssetBuilderSDK::AssetBuilderDesc shaderVariantAssetBuilder2Descriptor;
+ shaderVariantAssetBuilder2Descriptor.m_name = "Shader Variant Asset Builder 2";
+ // Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update
+ // ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder".
+ shaderVariantAssetBuilder2Descriptor.m_version = 1; // ATOM-15276
+ shaderVariantAssetBuilder2Descriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(
+ AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension2),
+ AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
+ shaderVariantAssetBuilder2Descriptor.m_busId = azrtti_typeid