Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,262 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzCore/Serialization/EditContext.h>
#include <Source/SceneProcessingModule.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IAnimationData.h>
#include <Config/SettingsObjects/NodeSoftNameSetting.h>
#include <Config/SettingsObjects/FileSoftNameSetting.h>
#include <Config/Components/SceneProcessingConfigSystemComponent.h>
#include <Config/Widgets/GraphTypeSelector.h>
namespace AZ
{
namespace SceneProcessingConfig
{
void SceneProcessingConfigSystemComponentSerializationEvents::OnWriteBegin(void* classPtr)
{
SceneProcessingConfigSystemComponent* component = reinterpret_cast<SceneProcessingConfigSystemComponent*>(classPtr);
component->Clear();
}
SceneProcessingConfigSystemComponent::SceneProcessingConfigSystemComponent()
{
using namespace AZ::SceneAPI::SceneCore;
ActivateSceneModule(SceneProcessing::s_sceneCoreModule);
ActivateSceneModule(SceneProcessing::s_sceneDataModule);
ActivateSceneModule(SceneProcessing::s_fbxSceneBuilderModule);
// Defaults in case there's no config setup in the Project Configurator.
m_softNames.push_back(aznew NodeSoftNameSetting("_lod1", PatternMatcher::MatchApproach::PostFix, "LODMesh1", true));
m_softNames.push_back(aznew NodeSoftNameSetting("_lod2", PatternMatcher::MatchApproach::PostFix, "LODMesh2", true));
m_softNames.push_back(aznew NodeSoftNameSetting("_lod3", PatternMatcher::MatchApproach::PostFix, "LODMesh3", true));
m_softNames.push_back(aznew NodeSoftNameSetting("_lod4", PatternMatcher::MatchApproach::PostFix, "LODMesh4", true));
m_softNames.push_back(aznew NodeSoftNameSetting("_lod5", PatternMatcher::MatchApproach::PostFix, "LODMesh5", true));
m_softNames.push_back(aznew NodeSoftNameSetting("_phys", PatternMatcher::MatchApproach::PostFix, "PhysicsMesh", true));
m_softNames.push_back(aznew NodeSoftNameSetting("_ignore", PatternMatcher::MatchApproach::PostFix, "Ignore", false));
// If the filename ends with "_anim" this will mark all nodes as "Ignore" unless they're derived from IAnimationData. This will
// cause only animations to be exported from the .fbx file even if there's other data available.
m_softNames.push_back(aznew FileSoftNameSetting("_anim", PatternMatcher::MatchApproach::PostFix, "Ignore", false,
{ FileSoftNameSetting::GraphType(SceneAPI::DataTypes::IAnimationData::TYPEINFO_Name()) }));
m_UseCustomNormals = true;
}
void SceneProcessingConfigSystemComponent::Activate()
{
SceneProcessingConfigRequestBus::Handler::BusConnect();
AZ::SceneAPI::Events::AssetImportRequestBus::Handler::BusConnect();
SceneProcessingConfig::GraphTypeSelector::Register();
}
void SceneProcessingConfigSystemComponent::Deactivate()
{
SceneProcessingConfig::GraphTypeSelector::Unregister();
AZ::SceneAPI::Events::AssetImportRequestBus::Handler::BusDisconnect();
SceneProcessingConfigRequestBus::Handler::BusDisconnect();
}
SceneProcessingConfigSystemComponent::~SceneProcessingConfigSystemComponent()
{
DeactivateSceneModule(SceneProcessing::s_fbxSceneBuilderModule);
DeactivateSceneModule(SceneProcessing::s_sceneDataModule);
DeactivateSceneModule(SceneProcessing::s_sceneCoreModule);
}
void SceneProcessingConfigSystemComponent::Clear()
{
m_softNames.clear();
m_softNames.shrink_to_fit();
m_UseCustomNormals = true;
}
const AZStd::vector<SoftNameSetting*>* SceneProcessingConfigSystemComponent::GetSoftNames()
{
return &m_softNames;
}
bool SceneProcessingConfigSystemComponent::AddSoftName(SoftNameSetting* newSoftname)
{
bool success = true;
Crc32 newHash = newSoftname->GetVirtualTypeHash();
for (SoftNameSetting* softName : m_softNames)
{
//First check whether an item with the same CRC value already exists.
if (newHash == softName->GetVirtualTypeHash())
{
AZ_Error("SceneProcessing", false, "newSoftname(%s) and existing softName(%s) have the same hash: 0x%X",
newSoftname->GetVirtualType().c_str(), softName->GetVirtualType().c_str(), newHash);
success = false;
break;
}
}
if (success)
{
m_softNames.push_back(newSoftname);
}
return success;
}
bool SceneProcessingConfigSystemComponent::AddNodeSoftName(const char* pattern,
SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool includeChildren)
{
SoftNameSetting* newSoftname = aznew NodeSoftNameSetting(pattern, approach, virtualType, includeChildren);
bool success = AddSoftName(newSoftname);
if (!success)
{
delete newSoftname;
}
return success;
}
bool SceneProcessingConfigSystemComponent::AddFileSoftName(const char* pattern,
SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool inclusive, const AZStd::string& graphObjectTypeName)
{
SoftNameSetting* newSoftname = aznew FileSoftNameSetting(pattern, approach, virtualType, inclusive,
{ FileSoftNameSetting::GraphType(graphObjectTypeName) });
bool success = AddSoftName(newSoftname);
if (!success)
{
delete newSoftname;
}
return success;
}
void SceneProcessingConfigSystemComponent::AreCustomNormalsUsed(bool &value)
{
value = m_UseCustomNormals;
}
void SceneProcessingConfigSystemComponent::Reflect(AZ::ReflectContext* context)
{
ReflectSceneModule(context, SceneProcessing::s_sceneCoreModule);
ReflectSceneModule(context, SceneProcessing::s_sceneDataModule);
ReflectSceneModule(context, SceneProcessing::s_fbxSceneBuilderModule);
SoftNameSetting::Reflect(context);
NodeSoftNameSetting::Reflect(context);
FileSoftNameSetting::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<SceneProcessingConfigSystemComponent, AZ::Component>()
->Version(2)
->EventHandler<SceneProcessingConfigSystemComponentSerializationEvents>()
->Field("softNames", &SceneProcessingConfigSystemComponent::m_softNames)
->Field("useCustomNormals", &SceneProcessingConfigSystemComponent::m_UseCustomNormals);
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<SceneProcessingConfigSystemComponent>("Scene Processing Config", "Use this component to fine tune the defaults for processing of scene files like Fbx.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Assets")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &SceneProcessingConfigSystemComponent::m_softNames,
"Soft naming conventions", "Update the naming conventions to suit your project.")
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->DataElement(AZ::Edit::UIHandlers::Default, &SceneProcessingConfigSystemComponent::m_UseCustomNormals,
"Use Custom Normals", "When enabled, Lumberyard will use the DCC assets custom or tangent space normals. When disabled, the normals will be averaged. This setting can be overridden on individual FBX asset settings.")
->Attribute(AZ::Edit::Attributes::AutoExpand, false);
}
}
}
void SceneProcessingConfigSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SceneProcessingConfigService", 0x7b333b47));
}
void SceneProcessingConfigSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("SceneProcessingConfigService", 0x7b333b47));
}
void SceneProcessingConfigSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
AZ_UNUSED(required);
}
void SceneProcessingConfigSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
AZ_UNUSED(dependent);
}
void SceneProcessingConfigSystemComponent::ReflectSceneModule(AZ::ReflectContext* context,
const AZStd::unique_ptr<AZ::DynamicModuleHandle>& module)
{
using ReflectFunc = void(*)(AZ::SerializeContext*);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
if (module)
{
ReflectFunc reflect = module->GetFunction<ReflectFunc>("Reflect");
if (reflect)
{
(*reflect)(serialize);
}
}
}
using ReflectBehaviorFunc = void(*)(AZ::BehaviorContext*);
AZ::BehaviorContext* behavior = azrtti_cast<AZ::BehaviorContext*>(context);
if (behavior)
{
if (module)
{
ReflectBehaviorFunc reflectBehavior = module->GetFunction<ReflectBehaviorFunc>("ReflectBehavior");
if (reflectBehavior)
{
(*reflectBehavior)(behavior);
}
}
}
}
void SceneProcessingConfigSystemComponent::ActivateSceneModule(const AZStd::unique_ptr<AZ::DynamicModuleHandle>& module)
{
using ActivateFunc = void(*)();
if (module)
{
ActivateFunc activate = module->GetFunction<ActivateFunc>("Activate");
if (activate)
{
(*activate)();
}
}
}
void SceneProcessingConfigSystemComponent::DeactivateSceneModule(const AZStd::unique_ptr<AZ::DynamicModuleHandle>& module)
{
using DeactivateFunc = void(*)();
if (module)
{
DeactivateFunc deactivate = module->GetFunction<DeactivateFunc>("Deactivate");
if (deactivate)
{
(*deactivate)();
}
}
}
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,85 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <SceneAPI/SceneCore/Components/SceneSystemComponent.h>
#include <Config/SceneProcessingConfigBus.h>
#include <Config/SettingsObjects/SoftNameSetting.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
namespace AZ
{
class DynamicModuleHandle;
namespace SceneProcessingConfig
{
class SceneProcessingConfigSystemComponentSerializationEvents
: public SerializeContext::IEventHandler
{
public:
AZ_CLASS_ALLOCATOR(SceneProcessingConfigSystemComponentSerializationEvents, SystemAllocator, 0);
void OnWriteBegin(void* classPtr) override;
};
class SceneProcessingConfigSystemComponent
: public AZ::SceneAPI::SceneCore::SceneSystemComponent
, protected SceneProcessingConfigRequestBus::Handler
, public AZ::SceneAPI::Events::AssetImportRequestBus::Handler
{
public:
AZ_COMPONENT(SceneProcessingConfigSystemComponent, "{80FE1130-91B4-44D4-869F-859BB996161A}", AZ::SceneAPI::SceneCore::SceneSystemComponent);
SceneProcessingConfigSystemComponent();
~SceneProcessingConfigSystemComponent();
void Activate() override;
void Deactivate() override;
void Clear();
// SceneProcessingConfigRequestBus START
const AZStd::vector<SoftNameSetting*>* GetSoftNames() override;
bool AddNodeSoftName(const char* pattern,
SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool includeChildren) override;
bool AddFileSoftName(const char* pattern, SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool inclusive, const AZStd::string& graphObjectTypeName) override;
// SceneProcessingConfigRequestBus END
void AreCustomNormalsUsed(bool &value) override;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(ComponentDescriptor::DependencyArrayType& dependent);
private:
/// It is the responsibility of the caller to delete newSoftname if this method returns
/// false.
bool AddSoftName(SoftNameSetting* newSoftname);
static void ReflectSceneModule(ReflectContext* context, const AZStd::unique_ptr<DynamicModuleHandle>& module);
static void ActivateSceneModule(const AZStd::unique_ptr<DynamicModuleHandle>& module);
static void DeactivateSceneModule(const AZStd::unique_ptr<DynamicModuleHandle>& module);
AZStd::vector<SoftNameSetting*> m_softNames;
bool m_UseCustomNormals;
};
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,82 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <Config/SceneProcessingConfigBus.h>
#include <Config/Components/SoftNameBehavior.h>
#include <Config/SettingsObjects/SoftNameSetting.h>
namespace AZ
{
namespace SceneProcessingConfig
{
void SoftNameBehavior::Activate()
{
SceneAPI::Events::GraphMetaInfoBus::Handler::BusConnect();
}
void SoftNameBehavior::Deactivate()
{
SceneAPI::Events::GraphMetaInfoBus::Handler::BusDisconnect();
}
void SoftNameBehavior::GetVirtualTypes(AZStd::set<Crc32>& types, const SceneAPI::Containers::Scene& scene,
SceneAPI::Containers::SceneGraph::NodeIndex node)
{
const AZStd::vector<SoftNameSetting*>* softNames = nullptr;
SceneProcessingConfigRequestBus::BroadcastResult(softNames, &SceneProcessingConfigRequestBus::Events::GetSoftNames);
if (softNames)
{
for (const SoftNameSetting* softName : *softNames)
{
if (types.find(softName->GetVirtualTypeHash()) != types.end())
{
// This type has already been added.
continue;
}
if (softName->IsVirtualType(scene, node))
{
types.insert(softName->GetVirtualTypeHash());
}
}
}
}
void SoftNameBehavior::GetVirtualTypeName(AZStd::string& name, Crc32 type)
{
if (type == AZ_CRC("Ignore", 0x0d88d6e2))
{
name = "Ignore";
}
}
void SoftNameBehavior::GetAllVirtualTypes(AZStd::set<Crc32>& types)
{
// Add types that aren't handled by one specific behavior and have a more global utility.
if (types.find(AZ_CRC("Ignore", 0x0d88d6e2)) == types.end())
{
types.insert(AZ_CRC("Ignore", 0x0d88d6e2));
}
}
void SoftNameBehavior::Reflect(ReflectContext* context)
{
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SoftNameBehavior, BehaviorComponent>()->Version(1);
}
}
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,42 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/SceneCore/Components/BehaviorComponent.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
namespace AZ
{
namespace SceneProcessingConfig
{
class SoftNameBehavior
: public SceneAPI::SceneCore::BehaviorComponent
, protected SceneAPI::Events::GraphMetaInfoBus::Handler
{
public:
AZ_COMPONENT(SoftNameBehavior, "{C2A9D207-485F-4752-B37B-388B0A52A956}", SceneAPI::SceneCore::BehaviorComponent);
~SoftNameBehavior() override = default;
void Activate() override;
void Deactivate() override;
void GetVirtualTypes(AZStd::set<Crc32>& types, const SceneAPI::Containers::Scene& scene,
SceneAPI::Containers::SceneGraph::NodeIndex node) override;
void GetVirtualTypeName(AZStd::string& name, Crc32 type) override;
void GetAllVirtualTypes(AZStd::set<Crc32>& types) override;
static void Reflect(ReflectContext* context);
};
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,198 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneCore/Utilities/PatternMatcher.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <Config/SettingsObjects/FileSoftNameSetting.h>
namespace AZ
{
namespace SceneProcessingConfig
{
FileSoftNameSetting::GraphType::GraphType()
: m_cachedId(Uuid::CreateNull())
{
}
FileSoftNameSetting::GraphType::GraphType(const AZStd::string& name)
: m_name(name)
, m_cachedId(Uuid::CreateNull())
{
}
FileSoftNameSetting::GraphType::GraphType(AZStd::string&& name)
: m_name(AZStd::move(name))
, m_cachedId(Uuid::CreateNull())
{
}
const AZStd::string& FileSoftNameSetting::GraphType::GetName() const
{
return m_name;
}
const Uuid& FileSoftNameSetting::GraphType::GetId() const
{
if (m_cachedId.IsNull())
{
SerializeContext* context = nullptr;
ComponentApplicationBus::BroadcastResult(context, &ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(context, "Unable to find valid serialize context.");
context->EnumerateDerived<SceneAPI::DataTypes::IGraphObject>(
[this](const SerializeContext::ClassData* data, const Uuid& typeId) -> bool
{
AZ_UNUSED(typeId);
if (AzFramework::StringFunc::Equal(data->m_name, m_name.c_str()))
{
m_cachedId = data->m_typeId;
return false;
}
return true;
});
if (m_cachedId.IsNull())
{
AZ_TracePrintf(SceneAPI::Utilities::WarningWindow, "Unable to find '%s' in the serialize context.", m_name.c_str());
}
}
return m_cachedId;
}
void FileSoftNameSetting::GraphType::Reflect(ReflectContext* context)
{
SerializeContext* serialize = azrtti_cast<SerializeContext*>(context);
if (serialize)
{
serialize->Class<GraphType>()
->Version(1)
->Field("name", &GraphType::m_name);
}
}
FileSoftNameSetting::GraphTypeContainer::GraphTypeContainer(std::initializer_list<GraphType> graphTypes)
: m_types(graphTypes)
{
}
AZStd::vector<FileSoftNameSetting::GraphType>& FileSoftNameSetting::GraphTypeContainer::GetGraphTypes()
{
return m_types;
}
const AZStd::vector<FileSoftNameSetting::GraphType>& FileSoftNameSetting::GraphTypeContainer::GetGraphTypes() const
{
return m_types;
}
void FileSoftNameSetting::GraphTypeContainer::Reflect(ReflectContext* context)
{
SerializeContext* serialize = azrtti_cast<SerializeContext*>(context);
if (serialize)
{
serialize->Class<GraphTypeContainer>()
->Version(1)
->Field("types", &GraphTypeContainer::m_types);
}
}
FileSoftNameSetting::FileSoftNameSetting(const char* pattern, SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool inclusive, std::initializer_list<GraphType> graphTypes)
: SoftNameSetting(pattern, approach, virtualType)
, m_inclusiveList(inclusive)
, m_graphTypes(graphTypes)
, m_cachedScene(nullptr)
{
}
bool FileSoftNameSetting::IsVirtualType(const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex node) const
{
bool nameMatch = false;
if (m_cachedScene == &scene)
{
nameMatch = m_cachedNameMatch;
}
else
{
switch (m_pattern.GetMatchApproach())
{
case SceneAPI::SceneCore::PatternMatcher::MatchApproach::PreFix:
nameMatch = m_pattern.MatchesPattern(scene.GetName());
break;
case SceneAPI::SceneCore::PatternMatcher::MatchApproach::PostFix:
nameMatch = m_pattern.MatchesPattern(scene.GetName());
break;
case SceneAPI::SceneCore::PatternMatcher::MatchApproach::Regex:
nameMatch = m_pattern.MatchesPattern(scene.GetSourceFilename());
break;
default:
AZ_Assert(false, "Unknown option '%i' for pattern matcher.", m_pattern.GetMatchApproach());
nameMatch = false;
}
m_cachedNameMatch = nameMatch;
m_cachedScene = &scene;
}
if (nameMatch)
{
AZStd::shared_ptr<const SceneAPI::DataTypes::IGraphObject> object = scene.GetGraph().GetNodeContent(node);
for (const GraphType& type : m_graphTypes.GetGraphTypes())
{
if (object->RTTI_IsTypeOf(type.GetId()))
{
return m_inclusiveList;
}
}
return !m_inclusiveList;
}
else
{
return false;
}
}
void FileSoftNameSetting::Reflect(ReflectContext* context)
{
GraphType::Reflect(context);
GraphTypeContainer::Reflect(context);
SerializeContext* serialize = azrtti_cast<SerializeContext*>(context);
if (serialize)
{
serialize->Class<FileSoftNameSetting, SoftNameSetting>()
->Version(1)
->Field("graphTypes", &FileSoftNameSetting::m_graphTypes)
->Field("inclusiveList", &FileSoftNameSetting::m_inclusiveList);
EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<FileSoftNameSetting>("File name setting", "Applies the pattern to the name of the scene file.")
->ClassElement(Edit::ClassElements::EditorData, "")
->Attribute(Edit::Attributes::AutoExpand, true)
->DataElement(AZ_CRC("GraphTypeSelector", 0x362ac245), &FileSoftNameSetting::m_graphTypes, "Graph type",
"The graph types that are the soft name applies to.")
->Attribute(Edit::Attributes::AutoExpand, true)
->DataElement(Edit::UIHandlers::Default, &FileSoftNameSetting::m_inclusiveList, "Inclusive",
"If true the types in the list will marked as the virtual type, otherwise any types that are NOT in the list.");
}
}
}
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,87 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <initializer_list>
#include <Config/SettingsObjects/SoftNameSetting.h>
namespace AZ
{
namespace SceneProcessingConfig
{
class FileSoftNameSetting : public SoftNameSetting
{
public:
class GraphType
{
public:
AZ_CLASS_ALLOCATOR(GraphType, AZ::SystemAllocator, 0);
AZ_RTTI(GraphType, "{368E85F4-4FF5-4708-82A1-FCDC993D4C34}");
GraphType();
explicit GraphType(const AZStd::string& name);
explicit GraphType(AZStd::string&& name);
virtual ~GraphType() = default;
const AZStd::string& GetName() const;
const Uuid& GetId() const;
static void Reflect(AZ::ReflectContext* context);
private:
AZStd::string m_name;
mutable Uuid m_cachedId;
};
// Wrapper around AZStd::vector<GraphType> for the sole purpose of forcing the reflected
// property editor to not use a container view.
class GraphTypeContainer
{
public:
AZ_CLASS_ALLOCATOR(GraphTypeContainer, AZ::SystemAllocator, 0);
AZ_RTTI(GraphTypeContainer, "{35E70739-CD31-43C2-A024-769755A26CAE}");
GraphTypeContainer() = default;
explicit GraphTypeContainer(std::initializer_list<GraphType> graphTypes);
virtual ~GraphTypeContainer() = default;
AZStd::vector<GraphType>& GetGraphTypes();
const AZStd::vector<GraphType>& GetGraphTypes() const;
static void Reflect(AZ::ReflectContext* context);
private:
AZStd::vector<GraphType> m_types;
};
AZ_CLASS_ALLOCATOR(FileSoftNameSetting, AZ::SystemAllocator, 0);
AZ_RTTI(FileSoftNameSetting, "{CED5FBF7-F74A-49E2-9FE0-DFF7EDA274CE}", SoftNameSetting);
FileSoftNameSetting() = default;
FileSoftNameSetting(const char* pattern, SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool inclusive, std::initializer_list<GraphType> graphTypes);
~FileSoftNameSetting() override = default;
bool IsVirtualType(const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex node) const;
static void Reflect(AZ::ReflectContext* context);
private:
GraphTypeContainer m_graphTypes;
bool m_inclusiveList;
mutable const SceneAPI::Containers::Scene* m_cachedScene;
mutable bool m_cachedNameMatch;
};
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,88 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/EditContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphUpwardsIterator.h>
#include <Config/SettingsObjects/NodeSoftNameSetting.h>
namespace AZ
{
namespace SceneProcessingConfig
{
NodeSoftNameSetting::NodeSoftNameSetting(const char* pattern, SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool includeChildren)
: SoftNameSetting(pattern, approach, virtualType)
, m_includeChildren(includeChildren)
{
}
void NodeSoftNameSetting::Reflect(ReflectContext* context)
{
SerializeContext* serialize = azrtti_cast<SerializeContext*>(context);
if (serialize)
{
serialize->Class<NodeSoftNameSetting, SoftNameSetting>()
->Version(1)
->Field("includeChildren", &NodeSoftNameSetting::m_includeChildren);
EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<NodeSoftNameSetting>("Node name setting", "Applies the pattern to the name of the node.")
->ClassElement(Edit::ClassElements::EditorData, "")
->Attribute(Edit::Attributes::AutoExpand, true)
->DataElement(Edit::UIHandlers::Default, &NodeSoftNameSetting::m_includeChildren,
"Include child nodes", "Whether or not the soft name only applies to the matching node or propagated to all its children as well.");
}
}
}
bool NodeSoftNameSetting::IsVirtualType(const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex node) const
{
const SceneAPI::Containers::SceneGraph& graph = scene.GetGraph();
if (m_includeChildren)
{
auto upwardsView = SceneAPI::Containers::Views::MakeSceneGraphUpwardsView(graph, node, graph.GetNameStorage().begin(), true);
for (const SceneAPI::Containers::SceneGraph::Name& name : upwardsView)
{
if (MatchesPattern(name))
{
return true;
}
}
return false;
}
else
{
return MatchesPattern(graph.GetNodeName(node));
}
}
bool NodeSoftNameSetting::MatchesPattern(const SceneAPI::Containers::SceneGraph::Name& name) const
{
switch (m_pattern.GetMatchApproach())
{
case SceneAPI::SceneCore::PatternMatcher::MatchApproach::PreFix:
return m_pattern.MatchesPattern(name.GetName(), name.GetNameLength());
case SceneAPI::SceneCore::PatternMatcher::MatchApproach::PostFix:
return m_pattern.MatchesPattern(name.GetPath(), name.GetPathLength());
case SceneAPI::SceneCore::PatternMatcher::MatchApproach::Regex:
return m_pattern.MatchesPattern(name.GetPath(), name.GetPathLength());
default:
AZ_Assert(false, "Unknown option '%i' for pattern matcher.", m_pattern.GetMatchApproach());
return false;
}
}
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Config/SettingsObjects/SoftNameSetting.h>
namespace AZ
{
namespace SceneProcessingConfig
{
class NodeSoftNameSetting : public SoftNameSetting
{
public:
AZ_CLASS_ALLOCATOR(NodeSoftNameSetting, SystemAllocator, 0);
AZ_RTTI(NodeSoftNameSetting, "{74629DAE-641A-4BCE-B6D5-3F7DD9F647FA}", SoftNameSetting);
NodeSoftNameSetting() = default;
NodeSoftNameSetting(const char* pattern, SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType, bool includeChildren);
~NodeSoftNameSetting() override = default;
bool IsVirtualType(const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex node) const override;
static void Reflect(AZ::ReflectContext* context);
private:
bool MatchesPattern(const SceneAPI::Containers::SceneGraph::Name& name) const;
bool m_includeChildren = false;
};
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,94 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/sort.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
#include <Config/SettingsObjects/SoftNameSetting.h>
namespace AZ
{
namespace SceneProcessingConfig
{
SoftNameSetting::SoftNameSetting(const char* pattern, SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType)
: m_pattern(pattern, approach)
, m_virtualType(virtualType)
{
}
SoftNameSetting::~SoftNameSetting() = default;
Crc32 SoftNameSetting::GetVirtualTypeHash() const
{
if (m_virtualTypeHash == Crc32())
{
m_virtualTypeHash = Crc32(m_virtualType.c_str());
}
return m_virtualTypeHash;
}
const AZStd::string& SoftNameSetting::GetVirtualType() const
{
return m_virtualType;
}
void SoftNameSetting::Reflect(ReflectContext* context)
{
SerializeContext* serialize = azrtti_cast<SerializeContext*>(context);
if (serialize)
{
serialize->Class<SoftNameSetting>()
->Version(1)
->Field("pattern", &SoftNameSetting::m_pattern)
->Field("virtualType", &SoftNameSetting::m_virtualType);
EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<SoftNameSetting>("Soft name setting", "A pattern matcher to setup project specific naming conventions.")
->ClassElement(Edit::ClassElements::EditorData, "")
->Attribute(Edit::Attributes::AutoExpand, true)
->Attribute(Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(Edit::UIHandlers::Default, &SoftNameSetting::m_pattern, "Pattern",
"The pattern the matcher will check against.")
->Attribute(Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(Edit::UIHandlers::ComboBox, &SoftNameSetting::m_virtualType, "Virtual Type",
"The node(s) will be converted to this type after their pattern matches.")
->Attribute(Edit::Attributes::StringList, &SoftNameSetting::GetAllVirtualTypes);
}
}
}
AZStd::vector<AZStd::string> SoftNameSetting::GetAllVirtualTypes() const
{
using namespace SceneAPI::Events;
AZStd::set<Crc32> virtualTypes;
GraphMetaInfoBus::Broadcast(&GraphMetaInfoBus::Events::GetAllVirtualTypes, virtualTypes);
AZStd::vector<AZStd::string> result;
for (Crc32 virtualType : virtualTypes)
{
AZStd::string virtualTypeName;
GraphMetaInfoBus::Broadcast(&GraphMetaInfoBus::Events::GetVirtualTypeName, virtualTypeName, virtualType);
AZ_Assert(!virtualTypeName.empty(), "No name found for virtual type with hash %i.", static_cast<u32>(virtualType));
result.emplace_back(AZStd::move(virtualTypeName));
}
AZStd::sort(result.begin(), result.end());
return result;
}
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,60 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/Utilities/PatternMatcher.h>
namespace AZ
{
namespace SceneAPI
{
namespace Containers
{
class Scene;
}
}
namespace SceneProcessingConfig
{
class SoftNameSetting
{
public:
AZ_CLASS_ALLOCATOR(SoftNameSetting, SystemAllocator, 0);
AZ_RTTI(SoftNameSetting, "{FE7AAAF6-8BA5-4599-B9A6-CC28026A6FFE}");
SoftNameSetting() = default;
SoftNameSetting(const char* pattern, SceneAPI::SceneCore::PatternMatcher::MatchApproach approach,
const char* virtualType);
virtual ~SoftNameSetting() = 0;
virtual const AZStd::string& GetVirtualType() const;
virtual Crc32 GetVirtualTypeHash() const;
virtual bool IsVirtualType(const SceneAPI::Containers::Scene& scene, SceneAPI::Containers::SceneGraph::NodeIndex node) const = 0;
static void Reflect(ReflectContext* context);
protected:
AZStd::vector<AZStd::string> GetAllVirtualTypes() const;
SceneAPI::SceneCore::PatternMatcher m_pattern;
AZStd::string m_virtualType;
mutable Crc32 m_virtualTypeHash;
};
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,167 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <QMenu>
#include <QEvent>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
#include <Config/Widgets/GraphTypeSelector.h>
namespace AZ
{
namespace SceneProcessingConfig
{
AZ_CLASS_ALLOCATOR_IMPL(GraphTypeSelector, SystemAllocator, 0)
GraphTypeSelector* GraphTypeSelector::s_instance = nullptr;
QWidget* GraphTypeSelector::CreateGUI(QWidget* parent)
{
QPushButton* base = new QPushButton("Select required graph types", parent);
QMenu* menu = new QMenu(base);
menu->setLayoutDirection(Qt::LeftToRight);
menu->setStyleSheet("border: none; background-color: #333333;");
SerializeContext* context = nullptr;
ComponentApplicationBus::BroadcastResult(context, &ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(context, "Unable to find valid serialize context.");
context->EnumerateDerived<SceneAPI::DataTypes::IGraphObject>(
[menu](const SerializeContext::ClassData* data, const Uuid& typeId) -> bool
{
AZ_UNUSED(typeId);
QAction* action = menu->addAction(data->m_name);
action->setCheckable(true);
return true;
});
base->setMenu(menu);
base->installEventFilter(this);
return base;
}
bool GraphTypeSelector::eventFilter(QObject* object, QEvent* event)
{
// Using FocusIn instead of FocusOut because after pressing the button the menu gets focus but after
// a selection is made the focus goes back to the button, so at that point saving needs to happen.
if (event->type() == QEvent::FocusIn)
{
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(
&AzToolsFramework::PropertyEditorGUIMessages::Bus::Events::RequestWrite, qobject_cast<QPushButton*>(object));
}
else if (event->type() == QEvent::Show)
{
QPushButton* button = qobject_cast<QPushButton*>(object);
button->menu()->setFixedWidth(button->width());
}
else if (event->type() == QEvent::Resize)
{
QPushButton* button = qobject_cast<QPushButton*>(object);
button->menu()->setFixedWidth(button->width());
}
return QObject::eventFilter(object, event);
}
u32 GraphTypeSelector::GetHandlerName() const
{
return AZ_CRC("GraphTypeSelector", 0x362ac245);
}
bool GraphTypeSelector::AutoDelete() const
{
return false;
}
bool GraphTypeSelector::IsDefaultHandler() const
{
return false;
}
void GraphTypeSelector::ConsumeAttribute(QPushButton* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
AZ_UNUSED(widget);
AZ_UNUSED(attrib);
AZ_UNUSED(attrValue);
AZ_UNUSED(debugName);
}
void GraphTypeSelector::WriteGUIValuesIntoProperty(size_t index, QPushButton* GUI,
property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
AZ_UNUSED(index);
AZ_UNUSED(node);
instance.GetGraphTypes().clear();
QMenu* menu = GUI->menu();
for (QAction* action : menu->actions())
{
if (action->isChecked())
{
instance.GetGraphTypes().emplace_back(action->text().toUtf8().constData());
}
}
}
bool GraphTypeSelector::ReadValuesIntoGUI(size_t index, QPushButton* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* node)
{
AZ_UNUSED(index);
AZ_UNUSED(node);
QMenu* menu = GUI->menu();
for (const auto& it : instance.GetGraphTypes())
{
for (QAction* action : menu->actions())
{
if (AzFramework::StringFunc::Equal(action->text().toUtf8().constData(), it.GetName().c_str()))
{
action->setChecked(true);
break;
}
}
}
return true;
}
void GraphTypeSelector::Register()
{
using namespace AzToolsFramework;
if (!s_instance)
{
s_instance = aznew GraphTypeSelector();
PropertyTypeRegistrationMessages::Bus::Broadcast(&PropertyTypeRegistrationMessages::Bus::Events::RegisterPropertyType, s_instance);
}
}
void GraphTypeSelector::Unregister()
{
using namespace AzToolsFramework;
if (s_instance)
{
PropertyTypeRegistrationMessages::Bus::Broadcast(&PropertyTypeRegistrationMessages::Bus::Events::UnregisterPropertyType, s_instance);
delete s_instance;
s_instance = nullptr;
}
}
} // namespace SceneProcessingConfig
} // namespace AZ
#include <Source/Config/Widgets/moc_GraphTypeSelector.cpp>
@@ -0,0 +1,59 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QPushButton>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <Config/SettingsObjects/FileSoftNameSetting.h>
#endif
class QWidget;
namespace AZ
{
namespace SceneProcessingConfig
{
class GraphTypeSelector
: public QObject
, public AzToolsFramework::PropertyHandler<FileSoftNameSetting::GraphTypeContainer, QPushButton>
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
QWidget* CreateGUI(QWidget* parent) override;
u32 GetHandlerName() const override;
bool AutoDelete() const;
bool IsDefaultHandler() const override;
void ConsumeAttribute(QPushButton* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, QPushButton* GUI, property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, QPushButton* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
static void Register();
static void Unregister();
private:
bool eventFilter(QObject* object, QEvent* event) override;
static GraphTypeSelector* s_instance;
};
} // namespace SceneProcessingConfig
} // namespace AZ
@@ -0,0 +1,318 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Exporting/Components/TangentGenerateComponent.h>
#include <Exporting/Components/TangentGenerators/MikkTGenerator.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexUVData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h>
#include <SceneAPI/SceneData/Rules/TangentsRule.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/ConvertIterator.h>
#include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h>
#include <SceneAPI/SceneData/GraphData/MeshVertexTangentData.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/std/smart_ptr/make_shared.h>
namespace AZ
{
namespace SceneExportingComponents
{
TangentGenerateComponent::TangentGenerateComponent()
{
BindToCall(&TangentGenerateComponent::GenerateTangentData);
}
void TangentGenerateComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<TangentGenerateComponent, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(1);
}
}
AZStd::vector<AZ::SceneAPI::DataTypes::TangentSpace> TangentGenerateComponent::CollectRequiredTangentSpaces(const AZ::SceneAPI::Containers::Scene& scene) const
{
AZStd::vector<AZ::SceneAPI::DataTypes::TangentSpace> result;
for (const auto& object : scene.GetManifest().GetValueStorage())
{
if (object->RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::IGroup::TYPEINFO_Uuid()))
{
const AZ::SceneAPI::DataTypes::IGroup* group = azrtti_cast<const AZ::SceneAPI::DataTypes::IGroup*>(object.get());
const AZ::SceneAPI::SceneData::TangentsRule* rule = group->GetRuleContainerConst().FindFirstByType<AZ::SceneAPI::SceneData::TangentsRule>().get();
if (rule)
{
if (AZStd::find(result.begin(), result.end(), rule->GetTangentSpace()) == result.end())
{
result.emplace_back(rule->GetTangentSpace());
}
}
}
}
return result;
}
AZ::SceneAPI::Events::ProcessingResult TangentGenerateComponent::GenerateTangentData(TangentGenerateContext& context)
{
// Iterate over all graph content and filter out all meshes.
const AZ::SceneAPI::Containers::SceneGraph& graph = context.m_scene.GetGraph();
AZ::SceneAPI::Containers::SceneGraph::ContentStorageConstData graphContent = graph.GetContentStorage();
// Build a list of mesh data nodes.
AZStd::vector<AZStd::pair<const AZ::SceneAPI::DataTypes::IMeshData*, AZ::SceneAPI::Containers::SceneGraph::NodeIndex> > meshes;
for (auto item = graphContent.begin(); item != graphContent.end(); ++item)
{
// Skip anything that isn't a mesh.
if (!(*item) || !(*item)->RTTI_IsTypeOf(AZ::SceneAPI::DataTypes::IMeshData::TYPEINFO_Uuid()))
{
continue;
}
// Get the mesh data and node index and store them in the vector as a pair, so we can iterate over them later.
const AZ::SceneAPI::DataTypes::IMeshData* mesh = static_cast<const AZ::SceneAPI::DataTypes::IMeshData*>(item->get());
AZ::SceneAPI::Containers::SceneGraph::NodeIndex nodeIndex = graph.ConvertToNodeIndex(item);
meshes.emplace_back(mesh, nodeIndex);
}
// Iterate over them. We had to build the array before as this method can insert new nodes, so using the iterator directly would fail.
for (auto& pairItem : meshes)
{
// Generate tangents for the mesh (if this is desired or needed).
const AZ::SceneAPI::DataTypes::IMeshData* mesh = pairItem.first;
AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex = pairItem.second;
if (!GenerateTangentsForMesh(context.m_scene, nodeIndex, const_cast<AZ::SceneAPI::DataTypes::IMeshData*>(mesh)))
{
AZ::SceneAPI::Events::ProcessingResult::Failure;
}
// Now that we have the tangents and bitangents, calculate the tangent w values for the ones that we imported from Fbx, as they only have xyz.
UpdateFbxTangentWValues(const_cast<AZ::SceneAPI::Containers::SceneGraph&>(graph), nodeIndex, mesh);
}
return AZ::SceneAPI::Events::ProcessingResult::Success;
}
void TangentGenerateComponent::UpdateFbxTangentWValues(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, const AZ::SceneAPI::DataTypes::IMeshData* meshData)
{
// Iterate over all UV sets.
AZ::SceneAPI::DataTypes::IMeshVertexUVData* uvData = AZ::SceneAPI::SceneData::TangentsRule::FindUVData(graph, nodeIndex, 0);
size_t uvSetIndex = 0;
while (uvData)
{
// Get the tangents and bitangents from Fbx.
AZ::SceneAPI::DataTypes::IMeshVertexTangentData* fbxTangentData = AZ::SceneAPI::SceneData::TangentsRule::FindTangentData(graph, nodeIndex, uvSetIndex, AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* fbxBitangentData = AZ::SceneAPI::SceneData::TangentsRule::FindBitangentData(graph, nodeIndex, uvSetIndex, AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
if (fbxTangentData && fbxBitangentData)
{
const size_t numVerts = uvData->GetCount();
AZ_Assert((numVerts == fbxTangentData->GetCount()) && (numVerts == fbxBitangentData->GetCount()), "Number of vertices inside UV set is not the same as number of tangents and bitangents.");
for (size_t i = 0; i < numVerts; ++i)
{
// This code calculates the best tangent.w value, which is either -1 or +1, depending on the bitangent being mirrored or not.
// We determine this by checking the angle between the generated tangent by doing a cross product between the tangent and normal, and the actual real bitangent.
// It is no guarantee that using "cross(normal, tangent.xyz)* tangent.w" will result in the right bitangent, as the basis might not be orthogonal.
// But we still go for the best guess.
AZ::Vector4 tangent = fbxTangentData->GetTangent(i);
AZ::Vector3 tangentDir = tangent.GetAsVector3();
tangentDir.NormalizeSafe();
AZ::Vector3 normal = meshData->GetNormal(static_cast<AZ::u32>(i));
normal.NormalizeSafe();
AZ::Vector3 generatedBitangent = normal.Cross(tangentDir);
float dot = fbxBitangentData->GetBitangent(i).Dot(generatedBitangent);
dot = AZ::GetMax(dot, -1.0f);
dot = AZ::GetMin(dot, 1.0f);
const float angle = acosf(dot);
if (angle > AZ::Constants::HalfPi)
{
tangent = fbxTangentData->GetTangent(i);
tangent.SetW(-1.0f);
}
else
{
tangent = fbxTangentData->GetTangent(i);
tangent.SetW(1.0f);
}
fbxTangentData->SetTangent(i, tangent);
}
}
// Find the next UV set.
uvData = AZ::SceneAPI::SceneData::TangentsRule::FindUVData(graph, nodeIndex, ++uvSetIndex);
}
}
bool TangentGenerateComponent::GenerateTangentsForMesh(AZ::SceneAPI::Containers::Scene& scene, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::SceneAPI::DataTypes::IMeshData* meshData)
{
AZ::SceneAPI::Containers::SceneGraph& graph = scene.GetGraph();
// Check if we have any UV data, if not, we cannot possibly generate the tangents.
AZ::SceneAPI::DataTypes::IMeshVertexUVData* uvData = AZ::SceneAPI::SceneData::TangentsRule::FindUVData(graph, nodeIndex, 0);
if (!uvData)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow, "We cannot generate tangents for this mesh, as it has no UV coordinates!\n");
return true; // No fatal error
}
// Check if we had tangents inside the Fbx file.
AZ::SceneAPI::DataTypes::IMeshVertexTangentData* fbxTangentData = AZ::SceneAPI::SceneData::TangentsRule::FindTangentData(graph, nodeIndex, 0, AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* fbxBitangentData = AZ::SceneAPI::SceneData::TangentsRule::FindBitangentData(graph, nodeIndex, 0, AZ::SceneAPI::DataTypes::TangentSpace::FromFbx);
// Check what tangent spaces we need.
AZStd::vector<AZ::SceneAPI::DataTypes::TangentSpace> requiredSpaces = CollectRequiredTangentSpaces(scene);
// If we have no tangent rules, so if the required spaces is empty.
if (requiredSpaces.empty())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Mesh '%s' has no tangents rule, assuming MikkT tangent space on UV set 0, using normalized tangents and orthogonal bitangents!\n", scene.GetGraph().GetNodeName(nodeIndex).GetName());
requiredSpaces.emplace_back(AZ::SceneAPI::DataTypes::TangentSpace::MikkT);
}
// If all we need is import from FBX, and we have tangent data from Fbx already, then skip generating.
if ((requiredSpaces.size() == 1 && requiredSpaces[0] == AZ::SceneAPI::DataTypes::TangentSpace::FromFbx) && fbxTangentData && fbxBitangentData)
{
return true;
}
// Generate all the tangent spaces we need.
// Do this for every UV set.
bool allSuccess = true;
size_t uvSetIndex = 0;
while (uvData)
{
for (AZ::SceneAPI::DataTypes::TangentSpace space : requiredSpaces)
{
switch (space)
{
// If we want Fbx tangents, we don't need to do anything for that.
case AZ::SceneAPI::DataTypes::TangentSpace::FromFbx:
{
allSuccess &= true;
}
break;
// Generate using MikkT space.
case AZ::SceneAPI::DataTypes::TangentSpace::MikkT:
{
allSuccess &= AZ::TangentGeneration::MikkT::GenerateTangents(scene.GetManifest(), graph, nodeIndex, const_cast<AZ::SceneAPI::DataTypes::IMeshData*>(meshData), uvSetIndex);
}
break;
// If we use EMotion FX calculated tangents, we don't need to generate this here.
case AZ::SceneAPI::DataTypes::TangentSpace::EMotionFX:
allSuccess &= true;
break;
default:
{
AZ_Assert(false, "Unknown tangent space selected (spaceID=%d) for UV set %d, cannot generate tangents!\n", static_cast<AZ::u32>(space), uvSetIndex);
allSuccess = false;
}
}
}
// Try to find the next UV set.
uvData = AZ::SceneAPI::SceneData::TangentsRule::FindUVData(graph, nodeIndex, ++uvSetIndex);
}
return allSuccess;
}
bool TangentGenerateComponent::CreateTangentBitangentLayers(AZ::SceneAPI::Containers::SceneManifest& manifest, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, size_t numVerts, size_t uvSetIndex, AZ::SceneAPI::DataTypes::TangentSpace tangentSpace, const char* spaceName, AZ::SceneAPI::Containers::SceneGraph& graph, AZ::SceneAPI::DataTypes::IMeshVertexTangentData** outTangentData, AZ::SceneAPI::DataTypes::IMeshVertexBitangentData** outBitangentData)
{
*outTangentData = nullptr;
*outBitangentData = nullptr;
//-------------------------------------------------------------
// Create tangent layer.
//-------------------------------------------------------------
AZStd::shared_ptr<SceneData::GraphData::MeshVertexTangentData> tangentData = AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexTangentData>();
tangentData->Resize(numVerts);
AZ_Assert(tangentData, "Failed to allocate tangent data for scene graph.");
if (!tangentData)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to allocate tangent data.\n");
return false;
}
tangentData->SetTangentSetIndex(uvSetIndex);
tangentData->SetTangentSpace(tangentSpace);
const AZStd::string tangentGeneratedName = AZStd::string::format("TangentSet_%s_%zu", spaceName, uvSetIndex);
const AZStd::string tangentSetName = AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName<SceneData::GraphData::MeshVertexBitangentData>(tangentGeneratedName, manifest);
AZ::SceneAPI::Containers::SceneGraph::NodeIndex newIndex = graph.AddChild(nodeIndex, tangentSetName.c_str(), tangentData);
AZ_Assert(newIndex.IsValid(), "Failed to create SceneGraph node for tangent attribute.");
if (!newIndex.IsValid())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to create node in scene graph that stores tangent data.\n");
return false;
}
graph.MakeEndPoint(newIndex);
//-------------------------------------------------------------
// Create bitangent layer.
//-------------------------------------------------------------
AZStd::shared_ptr<AZ::SceneData::GraphData::MeshVertexBitangentData> bitangentData = AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexBitangentData>();
bitangentData->Resize(numVerts);
AZ_Assert(bitangentData, "Failed to allocate bitangent data for scene graph.");
if (!bitangentData)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to allocate bitangent data.\n");
return false;
}
bitangentData->SetBitangentSetIndex(uvSetIndex);
bitangentData->SetTangentSpace(tangentSpace);
const AZStd::string bitangentGeneratedName = AZStd::string::format("BitangentSet_%s_%zu", spaceName, uvSetIndex);
const AZStd::string bitangentSetName = AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName<SceneData::GraphData::MeshVertexBitangentData>(bitangentGeneratedName, manifest);
newIndex = graph.AddChild(nodeIndex, bitangentSetName.c_str(), bitangentData);
AZ_Assert(newIndex.IsValid(), "Failed to create SceneGraph node for bitangent attribute.");
if (!newIndex.IsValid())
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to create node in scene graph that stores bitangent data.\n");
return false;
}
graph.MakeEndPoint(newIndex);
*outTangentData = tangentData.get();
*outBitangentData = bitangentData.get();
return true;
}
} // namespace SceneExportingComponents
} // namespace AZ
@@ -0,0 +1,73 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <RC/ResourceCompilerScene/Common/ExportContextGlobal.h>
#include <AzCore/RTTI/RTTI.h>
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IMeshData;
class IMeshVertexUVData;
class IMeshVertexTangentData;
class IMeshVertexBitangentData;
enum class TangentSpace;
}
}
namespace SceneExportingComponents
{
struct TangentGenerateContext
: public AZ::SceneAPI::Events::ICallContext
{
AZ_RTTI(TangentGenerateContext, "{E836F8F8-5A66-497C-89CC-2D37D741CCAA}", AZ::SceneAPI::Events::ICallContext);
TangentGenerateContext(AZ::SceneAPI::Containers::Scene& scene)
: m_scene(scene) {}
~TangentGenerateContext() override = default;
TangentGenerateContext& operator=(const TangentGenerateContext& other) = delete;
AZ::SceneAPI::Containers::Scene& m_scene;
};
class TangentGenerateComponent
: public AZ::SceneAPI::SceneCore::ExportingComponent
{
public:
AZ_COMPONENT(TangentGenerateComponent, "{57743E6F-8718-491C-8A82-24A6763904F5}", AZ::SceneAPI::SceneCore::ExportingComponent);
TangentGenerateComponent();
~TangentGenerateComponent() override = default;
static void Reflect(AZ::ReflectContext* context);
static bool CreateTangentBitangentLayers(AZ::SceneAPI::Containers::SceneManifest& manifest, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, size_t numVerts, size_t uvSetIndex, AZ::SceneAPI::DataTypes::TangentSpace tangentSpace,
const char* spaceName, AZ::SceneAPI::Containers::SceneGraph& graph, AZ::SceneAPI::DataTypes::IMeshVertexTangentData** outTangentData, AZ::SceneAPI::DataTypes::IMeshVertexBitangentData** outBitangentData);
AZ::SceneAPI::Events::ProcessingResult GenerateTangentData(TangentGenerateContext& context);
private:
bool GenerateTangentsForMesh(AZ::SceneAPI::Containers::Scene& scene, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::SceneAPI::DataTypes::IMeshData* meshData);
void UpdateFbxTangentWValues(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, const AZ::SceneAPI::DataTypes::IMeshData* meshData);
AZStd::vector<AZ::SceneAPI::DataTypes::TangentSpace> CollectRequiredTangentSpaces(const AZ::SceneAPI::Containers::Scene& scene) const;
};
}
}
@@ -0,0 +1,177 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Exporting/Components/TangentGenerators/MikkTGenerator.h>
#include <Exporting/Components/TangentGenerateComponent.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexUVData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h>
#include <SceneAPI/SceneData/GraphData/MeshVertexTangentData.h>
#include <SceneAPI/SceneData/Rules/TangentsRule.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <mikkelsen/mikktspace.h>
namespace AZ
{
namespace TangentGeneration
{
namespace MikkT
{
// Returns the number of triangles in the mesh.
int GetNumFaces(const SMikkTSpaceContext* context)
{
MikktCustomData* customData = static_cast<MikktCustomData*>(context->m_pUserData);
return customData->m_meshData->GetFaceCount();
}
int GetNumVerticesOfFace(const SMikkTSpaceContext* context, const int face)
{
AZ_UNUSED(context);
AZ_UNUSED(face);
return 3;
}
void GetPosition(const SMikkTSpaceContext* context, float posOut[], const int face, const int vert)
{
MikktCustomData* customData = static_cast<MikktCustomData*>(context->m_pUserData);
const AZ::u32 vertexIndex = customData->m_meshData->GetVertexIndex(face, vert);
const AZ::Vector3& pos = customData->m_meshData->GetPosition(vertexIndex);
posOut[0] = pos.GetX();
posOut[1] = pos.GetY();
posOut[2] = pos.GetZ();
}
void GetNormal(const SMikkTSpaceContext* context, float normOut[], const int face, const int vert)
{
MikktCustomData* customData = static_cast<MikktCustomData*>(context->m_pUserData);
const AZ::u32 vertexIndex = customData->m_meshData->GetVertexIndex(face, vert);
const AZ::Vector3 normal = customData->m_meshData->GetNormal(vertexIndex).GetNormalizedSafe();
normOut[0] = normal.GetX();
normOut[1] = normal.GetY();
normOut[2] = normal.GetZ();
}
void GetTexCoord(const SMikkTSpaceContext* context, float texOut[], const int face, const int vert)
{
MikktCustomData* customData = static_cast<MikktCustomData*>(context->m_pUserData);
const AZ::u32 vertexIndex = customData->m_meshData->GetVertexIndex(face, vert);
const AZ::Vector2& uv = customData->m_uvData->GetUV(vertexIndex);
texOut[0] = uv.GetX();
texOut[1] = uv.GetY();
}
// This function is used to return the tangent and signValue to the application.
// tangent is a unit length vector.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// bitangent = signValue * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
void SetTSpaceBasic(const SMikkTSpaceContext* context, const float tangent[], const float signValue, const int face, const int vert)
{
MikktCustomData* customData = static_cast<MikktCustomData*>(context->m_pUserData);
const AZ::u32 vertexIndex = customData->m_meshData->GetVertexIndex(face, vert);
AZ::Vector3 tangentVec3(tangent[0], tangent[1], tangent[2]);
tangentVec3.NormalizeSafe();
AZ::Vector3 normal = customData->m_meshData->GetNormal(vertexIndex);
normal.NormalizeSafe();
const AZ::Vector3 bitangent = normal.Cross(tangentVec3) * signValue;
customData->m_tangentData->SetTangent(vertexIndex, AZ::Vector4(tangentVec3.GetX(), tangentVec3.GetY(), tangentVec3.GetZ(), signValue));
customData->m_bitangentData->SetBitangent(vertexIndex, bitangent);
}
// This function is used to return tangent space results to the application.
// tangent and bitangent are unit length vectors and magS and magT are their
// true magnitudes which can be used for relief mapping effects.
// bitangent is the "real" bitangent and thus may not be perpendicular to tangent.
// However, both are perpendicular to the vertex normal.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// signValue = isOrientationPreserving ? 1.0f : -1.0f;
// bitangent = signValue * cross(vN, tangent);
void SetTSpace(const SMikkTSpaceContext* context, const float tangent[], const float bitangent[], const float magS, const float magT, const tbool isOrientationPreserving, const int face, const int vert)
{
MikktCustomData* customData = static_cast<MikktCustomData*>(context->m_pUserData);
const AZ::u32 vertexIndex = customData->m_meshData->GetVertexIndex(face, vert);
const float flipSign = isOrientationPreserving ? 1.0f : -1.0f;
const AZ::Vector4 tangentVec(tangent[0]*magS, tangent[1]*magS, tangent[2]*magS, flipSign);
const AZ::Vector3 bitangentVec(bitangent[0]*magT, bitangent[1]*magT, bitangent[2]*magT);
customData->m_tangentData->SetTangent(vertexIndex, tangentVec);
customData->m_bitangentData->SetBitangent(vertexIndex, bitangentVec);
}
bool GenerateTangents(AZ::SceneAPI::Containers::SceneManifest& manifest, AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::SceneAPI::DataTypes::IMeshData* meshData, size_t uvSet)
{
// Create tangent and bitangent data sets and relate them to the given UV set.
AZ::SceneAPI::DataTypes::IMeshVertexUVData* uvData = AZ::SceneAPI::SceneData::TangentsRule::FindUVData(graph, nodeIndex, uvSet);
AZ::SceneAPI::DataTypes::IMeshVertexTangentData* tangentData = nullptr;
AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* bitangentData = nullptr;
if (!uvData)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Cannot find UV data (set index=%d) to generate tangents and bitangents from in MikkT generator!\n", uvSet);
return false;
}
if (!AZ::SceneExportingComponents::TangentGenerateComponent::CreateTangentBitangentLayers(manifest, nodeIndex, meshData->GetVertexCount(), uvSet, AZ::SceneAPI::DataTypes::TangentSpace::MikkT, "MikkT", graph, &tangentData, &bitangentData))
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to create tangents and bitangents data sets inside MikkT generator!\n");
return false;
}
//----------------------------------
// Provide the MikkT interface.
SMikkTSpaceInterface mikkInterface;
mikkInterface.m_getNumFaces = GetNumFaces;
mikkInterface.m_getNormal = GetNormal;
mikkInterface.m_getPosition = GetPosition;
mikkInterface.m_getTexCoord = GetTexCoord;
mikkInterface.m_setTSpace = SetTSpace;
mikkInterface.m_setTSpaceBasic = nullptr;//SetTSpaceBasic;
mikkInterface.m_getNumVerticesOfFace= GetNumVerticesOfFace;
// Set the MikkT custom data.
MikktCustomData customData;
customData.m_meshData = meshData;
customData.m_uvData = uvData;
customData.m_tangentData = tangentData;
customData.m_bitangentData = bitangentData;
// Generate the tangents.
SMikkTSpaceContext mikkContext;
mikkContext.m_pInterface = &mikkInterface;
mikkContext.m_pUserData = &customData;
if (genTangSpaceDefault(&mikkContext) == 0)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Failed to generate tangents and bitangents using MikkT, because MikkT reported failure!\n");
return false;
}
return true;
}
} // namespace MikkT
} // namespace TangentGenerators
} // namespace AZ
@@ -0,0 +1,47 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/SceneCore/Containers/Scene.h>
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IMeshData;
class IMeshVertexUVData;
class IMeshVertexTangentData;
class IMeshVertexBitangentData;
enum class TangentSpace;
}
}
namespace TangentGeneration
{
namespace MikkT
{
struct MikktCustomData
{
AZ::SceneAPI::DataTypes::IMeshData* m_meshData;
AZ::SceneAPI::DataTypes::IMeshVertexUVData* m_uvData;
AZ::SceneAPI::DataTypes::IMeshVertexTangentData* m_tangentData;
AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* m_bitangentData;
};
// The main generation method.
bool GenerateTangents(AZ::SceneAPI::Containers::SceneManifest& manifest, AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::SceneAPI::DataTypes::IMeshData* meshData, size_t uvSet);
}
}
}
@@ -0,0 +1,51 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Exporting/Components/TangentPreExportComponent.h>
#include <Exporting/Components/TangentGenerateComponent.h>
namespace AZ
{
namespace SceneExportingComponents
{
namespace SceneEvents = AZ::SceneAPI::Events;
//namespace SceneUtil = AZ::SceneAPI::Utilities;
namespace SceneContainers = AZ::SceneAPI::Containers;
namespace SceneViews = AZ::SceneAPI::Containers::Views;
namespace SceneDataTypes = AZ::SceneAPI::DataTypes;
TangentPreExportComponent::TangentPreExportComponent()
{
BindToCall(&TangentPreExportComponent::Register);
}
void TangentPreExportComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<TangentPreExportComponent, AZ::SceneAPI::SceneCore::ExportingComponent>()->Version(1);
}
}
AZ::SceneAPI::Events::ProcessingResult TangentPreExportComponent::Register(AZ::SceneAPI::Events::PreExportEventContext& context)
{
SceneEvents::ProcessingResultCombiner result;
TangentGenerateContext tangentGenerateContext(const_cast<AZ::SceneAPI::Containers::Scene&>(context.GetScene()));
result += SceneEvents::Process<TangentGenerateContext>(tangentGenerateContext);
return SceneEvents::ProcessingResult::Success;
}
} // namespace SceneExportingComponents
} // namespace AZ
@@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <RC/ResourceCompilerScene/Common/ExportContextGlobal.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <AzCore/RTTI/RTTI.h>
namespace AZ
{
namespace SceneExportingComponents
{
class TangentPreExportComponent
: public AZ::SceneAPI::SceneCore::ExportingComponent
{
public:
AZ_COMPONENT(TangentPreExportComponent, "{BFFE114A-2FC6-42F1-92C4-61329CC54A2B}", AZ::SceneAPI::SceneCore::ExportingComponent);
TangentPreExportComponent();
~TangentPreExportComponent() override = default;
static void Reflect(AZ::ReflectContext* context);
AZ::SceneAPI::Events::ProcessingResult Register(AZ::SceneAPI::Events::PreExportEventContext& context);
};
}
}
@@ -0,0 +1,16 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
# Platform specific cmake file for configuring target compiler/link properties
# based on the active platform
# NOTE: functions in cmake are global, therefore adding functions to this file
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files
@@ -0,0 +1,85 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SceneBuilder/SceneBuilderComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Component/Entity.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <SceneAPI/SceneCore/Components/SceneSystemComponent.h>
#include <SceneAPI/SceneCore/Components/Utilities/EntityConstructor.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
namespace SceneBuilder
{
void BuilderPluginComponent::Activate()
{
using namespace AZ::SceneAPI::Events;
AZStd::unordered_set<AZStd::string> extensions;
AssetImportRequestBus::Broadcast(&AssetImportRequestBus::Events::GetSupportedFileExtensions, extensions);
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
builderDescriptor.m_name = "Scene Builder";
for (const AZStd::string& extension : extensions)
{
if (extension.empty())
{
continue;
}
AZStd::string pattern = AZStd::string::format((extension[0] == '.' ? "*%s" : "*.%s"), extension.c_str());
builderDescriptor.m_patterns.emplace_back(AZStd::move(pattern), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
}
builderDescriptor.m_busId = SceneBuilderWorker::GetUUID();
builderDescriptor.m_createJobFunction = AZStd::bind(&SceneBuilderWorker::CreateJobs, &m_sceneBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_processJobFunction = AZStd::bind(&SceneBuilderWorker::ProcessJob, &m_sceneBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_version = 4; // bump this to rebuild everything.
builderDescriptor.m_analysisFingerprint = m_sceneBuilder.GetFingerprint(); // bump this to at least re-analyze everything.
m_sceneBuilder.BusConnect(builderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Events::RegisterBuilderInformation, builderDescriptor);
AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Creating entity with scene system components.\n");
AZ::Entity* sceneSystemEntity = AZ::SceneAPI::SceneCore::EntityConstructor::BuildSceneSystemEntity();
AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, sceneSystemEntity, "Unable to create a system component for the SceneAPI.\n");
if (sceneSystemEntity)
{
sceneSystemEntity->Init();
sceneSystemEntity->Activate();
}
}
void BuilderPluginComponent::Deactivate()
{
m_sceneBuilder.BusDisconnect();
}
void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<BuilderPluginComponent, AZ::Component>()->Version(1)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }));
}
}
} // namespace SceneBuilder
@@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <SceneBuilder/SceneBuilderWorker.h>
namespace AZ
{
class Entity;
} // namespace AZ
namespace SceneBuilder
{
class BuilderPluginComponent
: public AZ::Component
{
public:
AZ_COMPONENT(BuilderPluginComponent, "{47BB00DE-2C6F-4A8E-9DCF-9A226DF0D649}")
static void Reflect(AZ::ReflectContext* context);
void Activate() override;
void Deactivate() override;
private:
SceneBuilderWorker m_sceneBuilder;
};
} // namespace SceneBuilder
@@ -0,0 +1,426 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Serialization/Utils.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/set.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
#include <SceneAPI/SceneCore/Components/GenerationComponent.h>
#include <SceneAPI/SceneCore/Components/LoadingComponent.h>
#include <SceneAPI/SceneCore/Components/Utilities/EntityConstructor.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Events/GenerateEventContext.h>
#include <SceneAPI/SceneCore/Events/ExportProductList.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Events/SceneSerializationBus.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/SceneBuilderDependencyBus.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IAnimationData.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <SceneBuilder/SceneBuilderWorker.h>
#include <SceneBuilder/TraceMessageHook.h>
namespace SceneBuilder
{
void SceneBuilderWorker::ShutDown()
{
m_isShuttingDown = true;
}
const char* SceneBuilderWorker::GetFingerprint() const
{
if (m_cachedFingerprint.empty())
{
// put them in an ORDERED set so that changing the reflection
// or the gems loaded does not invalidate FBX files due to order of reflection changing.
AZStd::set<AZStd::string> fragments;
AZ::SerializeContext* context = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
if (context)
{
auto callback = [&fragments](const AZ::SerializeContext::ClassData* data, const AZ::Uuid& typeId)
{
AZ_UNUSED(typeId);
fragments.insert(AZStd::string::format("[%s:v%i]", data->m_name, data->m_version));
return true;
};
context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::ExportingComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::ExportingComponent>());
context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::GenerationComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::GenerationComponent>());
context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::LoadingComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::LoadingComponent>());
}
for (const AZStd::string& element : fragments)
{
m_cachedFingerprint.append(element);
}
}
return m_cachedFingerprint.c_str();
}
void SceneBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
{
// Check for shutdown
if (m_isShuttingDown)
{
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
return;
}
for (auto& enabledPlatform : request.m_enabledPlatforms)
{
AssetBuilderSDK::JobDescriptor descriptor;
descriptor.m_jobKey = "Scene compilation";
descriptor.SetPlatformIdentifier(enabledPlatform.m_identifier.c_str());
descriptor.m_failOnError = true;
descriptor.m_priority = 11; // more important than static mesh files, since these may control logic (actors and motions specifically)
descriptor.m_additionalFingerprintInfo = GetFingerprint();
AZ::SceneAPI::SceneBuilderDependencyBus::Broadcast(&AZ::SceneAPI::SceneBuilderDependencyRequests::ReportJobDependencies,
descriptor.m_jobDependencyList, enabledPlatform.m_identifier.c_str());
response.m_createJobOutputs.push_back(descriptor);
}
// Adding corresponding material file as a source file dependency
AssetBuilderSDK::SourceFileDependency sourceFileDependencyInfo;
AZStd::string relPath = request.m_sourceFile.c_str();
AzFramework::StringFunc::Path::ReplaceExtension(relPath, "mtl");
sourceFileDependencyInfo.m_sourceFileDependencyPath = relPath;
response.m_sourceFileDependencyList.push_back(sourceFileDependencyInfo);
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
}
void SceneBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
using namespace AZ::SceneAPI::Containers;
// Only used during processing to redirect trace printfs with an warning or error window to the appropriate reporting function.
TraceMessageHook messageHook;
// Load Scene graph and manifest from the provided path and then initialize them.
if (m_isShuttingDown)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Loading scene was cancelled.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
AZStd::shared_ptr<Scene> scene;
if (!LoadScene(scene, request, response))
{
return;
}
// Run scene generation step to allow for runtime generation of SceneGraph objects
if (m_isShuttingDown)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Generation of dynamic scene objects was cancelled.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
if (!GenerateScene(scene.get(), request, response))
{
return;
}
// Process the scene.
if (m_isShuttingDown)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Processing scene was cancelled.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
return;
}
if (!ExportScene(scene, request, response))
{
return;
}
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Finalizing scene processing.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
}
AZ::Uuid SceneBuilderWorker::GetUUID()
{
return AZ::Uuid::CreateString("{BD8BF658-9485-4FE3-830E-8EC3A23C35F3}");
}
void SceneBuilderWorker::PopulateProductDependencies(const AZ::SceneAPI::Events::ExportProduct& exportProduct, const char* watchFolder, AssetBuilderSDK::JobProduct& jobProduct) const
{
// Register the product dependencies and path dependencies from the export product to the job product.
for (const AZ::SceneAPI::Events::ExportProduct& dependency : exportProduct.m_productDependencies)
{
jobProduct.m_dependencies.emplace_back(
AZ::Data::AssetId(dependency.m_id, dependency.m_subId.value_or(0)),
dependency.m_dependencyFlags);
}
for (const AZStd::string& pathDependency : exportProduct.m_legacyPathDependencies)
{
// SceneCore doesn't have access to AssetBuilderSDK, so it doesn't have access to the
// ProductPathDependency type or the ProductPathDependencyType enum. Exporters registered with the
// Scene Builder should report path dependencies on source files as absolute paths, while dependencies
// on product files should be reported as relative paths.
if (AzFramework::StringFunc::Path::IsRelative(pathDependency.c_str()))
{
// Make sure the path is relative to the watch folder. Paths passed in might be using asset database separators.
// Convert to system separators for path manipulation.
AZStd::string normalizedPathDependency = pathDependency;
AZStd::string normalizedWatchFolder(watchFolder);
AZStd::string assetRootRelativePath;
AzFramework::StringFunc::Path::Normalize(normalizedWatchFolder);
AzFramework::StringFunc::Path::Normalize(normalizedPathDependency);
AzFramework::StringFunc::Path::Join(normalizedWatchFolder.c_str(), normalizedPathDependency .c_str(), assetRootRelativePath);
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::Bus::Events::MakePathRelative, assetRootRelativePath, watchFolder);
jobProduct.m_pathDependencies.emplace(assetRootRelativePath, AssetBuilderSDK::ProductPathDependencyType::ProductFile);
}
else
{
jobProduct.m_pathDependencies.emplace(pathDependency, AssetBuilderSDK::ProductPathDependencyType::SourceFile);
}
}
jobProduct.m_dependenciesHandled = true; // We've populated the dependencies immediately above so it's OK to tell the AP we've handled dependencies
}
bool SceneBuilderWorker::LoadScene(AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& result,
const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
using namespace AZ::SceneAPI;
using namespace AZ::SceneAPI::Containers;
using namespace AZ::SceneAPI::Events;
AZ_TracePrintf(Utilities::LogWindow, "Loading scene.\n");
SceneSerializationBus::BroadcastResult(result, &SceneSerializationBus::Events::LoadScene, request.m_fullPath, request.m_sourceFileUUID);
if (!result)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load scene file.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return false;
}
AZ_TraceContext("Manifest", result->GetManifestFilename());
if (result->GetManifest().IsEmpty())
{
AZ_TracePrintf(Utilities::WarningWindow, "No manifest loaded and not enough information to create a default manifest.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
return false; // Still return false as there's no work so should exit.
}
return true;
}
bool SceneBuilderWorker::GenerateScene(AZ::SceneAPI::Containers::Scene* scene,
const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
using namespace AZ::SceneAPI;
using namespace AZ::SceneAPI::Events;
using namespace AZ::SceneAPI::SceneCore;
const char* platformIdentifier = request.m_jobDescription.GetPlatformIdentifier().c_str();
AZ_TracePrintf(Utilities::LogWindow, "Creating generate entities.\n");
EntityConstructor::EntityPointer exporter = EntityConstructor::BuildEntity("Scene Generation", azrtti_typeid<GenerationComponent>());
ProcessingResultCombiner result;
AZ_TracePrintf(Utilities::LogWindow, "Preparing for scene generation.\n");
result += Process<PreGenerateEventContext>(*scene, platformIdentifier);
AZ_TracePrintf(Utilities::LogWindow, "Generating...\n");
result += Process<GenerateEventContext>(*scene, platformIdentifier);
AZ_TracePrintf(Utilities::LogWindow, "Finalizing scene generation.\n");
result += Process<PostGenerateEventContext>(*scene, platformIdentifier);
if (result.GetResult() == ProcessingResult::Failure)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failure during scene generation.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return false;
}
return true;
}
bool SceneBuilderWorker::ExportScene(const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene,
const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
{
using namespace AZ::SceneAPI;
using namespace AZ::SceneAPI::Events;
using namespace AZ::SceneAPI::SceneCore;
AZ_Assert(scene, "Invalid scene passed for exporting.");
const AZStd::string& outputFolder = request.m_tempDirPath;
const char* platformIdentifier = request.m_jobDescription.GetPlatformIdentifier().c_str();
AZ_TraceContext("Output folder", outputFolder.c_str());
AZ_TraceContext("Platform", platformIdentifier);
AZ_TracePrintf(Utilities::LogWindow, "Processing scene.\n");
AZ_TracePrintf(Utilities::LogWindow, "Creating export entities.\n");
EntityConstructor::EntityPointer exporter = EntityConstructor::BuildEntity("Scene Exporters", ExportingComponent::TYPEINFO_Uuid());
ExportProductList productList;
ProcessingResultCombiner result;
AZ_TracePrintf(Utilities::LogWindow, "Preparing for export.\n");
result += Process<PreExportEventContext>(productList, outputFolder, *scene, platformIdentifier);
AZ_TracePrintf(Utilities::LogWindow, "Exporting...\n");
result += Process<ExportEventContext>(productList, outputFolder, *scene, platformIdentifier);
AZ_TracePrintf(Utilities::LogWindow, "Finalizing export process.\n");
result += Process<PostExportEventContext>(productList, outputFolder, platformIdentifier);
auto itr = request.m_jobDescription.m_jobParameters.find(AZ_CRC_CE("DebugFlag"));
if (itr != request.m_jobDescription.m_jobParameters.end() && itr->second == "true")
{
BuildDebugSceneGraph(outputFolder.c_str(), productList, scene);
}
AZ_TracePrintf(Utilities::LogWindow, "Collecting and registering products.\n");
for (const ExportProduct& product : productList.GetProducts())
{
const AZ::u32 subId = product.m_subId.has_value() ? product.m_subId.value() : BuildSubId(product);
AZ_TracePrintf(Utilities::LogWindow, "Listed product: %s+0x%08x - %s (type %s)\n", product.m_id.ToString<AZStd::string>().c_str(),
subId, product.m_filename.c_str(), product.m_assetType.ToString<AZStd::string>().c_str());
AssetBuilderSDK::JobProduct jobProduct(product.m_filename, product.m_assetType, subId);
PopulateProductDependencies(product, request.m_watchFolder.c_str(), jobProduct);
response.m_outputProducts.emplace_back(jobProduct);
// Unlike the version in ResourceCompilerScene/SceneCompiler.cpp, this version doesn't need to deal with sub ids that were
// created before explicit sub ids were added to the SceneAPI.
}
switch (result.GetResult())
{
case ProcessingResult::Success:
return true;
case ProcessingResult::Ignored:
// While ResourceCompilerScene is still around there's situations where either this builder or RCScene does work but the other not.
// That used to be a cause for a warning and will be again once RCScene has been removed. It's not possible to detect if either
// did any work so the warning is disabled for now.
// AZ_TracePrintf(Utilities::WarningWindow, "Nothing found to convert and export.\n");
return true;
case ProcessingResult::Failure:
AZ_TracePrintf(Utilities::ErrorWindow, "Failure during conversion and exporting.\n");
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return false;
default:
AZ_TracePrintf(Utilities::ErrorWindow,
"Unexpected result from conversion and exporting (%i).\n", result.GetResult());
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
return false;
}
return true;
}
// BuildSubId has an equivalent counterpart in ResourceCompilerScene. Both need to remain the same to avoid problems with sub ids.
AZ::u32 SceneBuilderWorker::BuildSubId(const AZ::SceneAPI::Events::ExportProduct& product) const
{
// Instead of the just the lower 16-bits, use the full 32-bits that are available. There are production examples of
// uber-fbx files that contain hundreds of meshes that need to be split into individual mesh objects as an example.
AZ::u32 id = static_cast<AZ::u32>(product.m_id.GetHash());
if (product.m_lod.has_value())
{
AZ::u8 lod = product.m_lod.value();
if (lod > 0xF)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow, "%i is too large to fit in the allotted bits for LOD.\n", static_cast<AZ::u32>(lod));
lod = 0xF;
}
// The product uses lods so mask out the lod bits and set them appropriately.
id &= ~AssetBuilderSDK::SUBID_MASK_LOD_LEVEL;
id |= lod << AssetBuilderSDK::SUBID_LOD_LEVEL_SHIFT;
}
return id;
}
void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, strToWrite);
dbgFile.Write(strToWrite, strlen(strToWrite));
dbgFile.Write("\n", strlen("\n"));
}
void SceneBuilderWorker::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene) const
{
const int debugSceneGraphVersion = 1;
AZStd::string productName, debugSceneFile;
AzFramework::StringFunc::Path::GetFullFileName(scene->GetSourceFilename().c_str(), productName);
AzFramework::StringFunc::Path::ReplaceExtension(productName, "dbgsg");
AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile);
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "outputFolder %s, name %s.\n", outputFolder, productName.c_str());
AZ::IO::SystemFile dbgFile;
if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", debugSceneGraphVersion).c_str());
WriteAndLog(dbgFile, scene->GetName().c_str());
const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph();
const AZ::SceneAPI::Containers::SceneGraph::NodeHeader* nodeIterator = sceneGraph.ConvertToHierarchyIterator(sceneGraph.GetRoot());
auto names = sceneGraph.GetNameStorage();
auto content = sceneGraph.GetContentStorage();
auto pairView = AZ::SceneAPI::Containers::Views::MakePairView(names, content);
auto view = AZ::SceneAPI::Containers::Views::MakeSceneGraphDownwardsView<
AZ::SceneAPI::Containers::Views::BreadthFirst>(
sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
for (auto&& viewIt : view)
{
if (viewIt.second == nullptr)
{
continue;
}
AZ::SceneAPI::DataTypes::IGraphObject* graphObject = const_cast<AZ::SceneAPI::DataTypes::IGraphObject*>(viewIt.second.get());
WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str());
AZ::SceneAPI::Utilities::DebugOutput debugOutput;
viewIt.second->GetDebugOutput(debugOutput);
if (!debugOutput.GetOutput().empty())
{
WriteAndLog(dbgFile, debugOutput.GetOutput().c_str());
}
}
dbgFile.Close();
static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}");
productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType,
AZStd::nullopt, AZStd::nullopt);
}
}
} // namespace SceneBuilder
@@ -0,0 +1,85 @@
/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
namespace AssetBuilderSDK
{
struct CreateJobsRequest;
struct CreateJobsResponse;
struct ProcessJobRequest;
struct ProcessJobResponse;
struct JobProduct;
}
namespace AZ
{
namespace SceneAPI
{
namespace Containers
{
class Scene;
}
namespace Events
{
struct ExportProduct;
class ExportProductList;
}
}
}
namespace SceneBuilder
{
class SceneBuilderWorker
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
~SceneBuilderWorker() override = default;
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
void ShutDown() override;
const char* GetFingerprint() const;
static AZ::Uuid GetUUID();
void PopulateProductDependencies(const AZ::SceneAPI::Events::ExportProduct& exportProduct, const char* watchFolder, AssetBuilderSDK::JobProduct& jobProduct) const;
protected:
void BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene) const;
bool LoadScene(AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& result,
const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
// @brief Execute runtime modifications to the Scene graph
//
// This step is run after the scene is loaded, but before the scene
// is exported. It emits events with the GenerateEventContext.
// Event handlers bound to that event can apply arbitrary
// transformations to the Scene, adding new nodes, replacing nodes,
// or removing nodes.
bool GenerateScene(AZ::SceneAPI::Containers::Scene* result,
const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
bool ExportScene(const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene,
const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
AZ::u32 BuildSubId(const AZ::SceneAPI::Events::ExportProduct& product) const;
bool m_isShuttingDown = false;
mutable AZStd::string m_cachedFingerprint;
};
} // namespace SceneBuilder
@@ -0,0 +1,97 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SceneBuilder/SceneSerializationHandler.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace SceneBuilder
{
void SceneSerializationHandler::Activate()
{
BusConnect();
}
void SceneSerializationHandler::Deactivate()
{
BusDisconnect();
}
void SceneSerializationHandler::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SceneSerializationHandler, AZ::Component>()->Version(1)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
;
}
}
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> SceneSerializationHandler::LoadScene(
const AZStd::string& filePath, AZ::Uuid sceneSourceGuid)
{
namespace Utilities = AZ::SceneAPI::Utilities;
using AZ::SceneAPI::Events::AssetImportRequest;
AZ_TraceContext("File", filePath);
if (sceneSourceGuid.IsNull())
{
AZ_TracePrintf(Utilities::ErrorWindow, "Invalid source guid for the scene file.");
return nullptr;
}
if (AZ::SceneAPI::Events::AssetImportRequest::IsManifestExtension(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Provided path contains the manifest path, not the path to the source file.");
return nullptr;
}
if (!AZ::SceneAPI::Events::AssetImportRequest::IsSceneFileExtension(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Provided path doesn't contain an extension supported by the SceneAPI.");
return nullptr;
}
if (AzFramework::StringFunc::Path::IsRelative(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "Given file path is relative where an absolute path was expected.");
return nullptr;
}
if (!AZ::IO::SystemFile::Exists(filePath.c_str()))
{
AZ_TracePrintf(Utilities::ErrorWindow, "No file exists at given source path.");
return nullptr;
}
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> scene =
AssetImportRequest::LoadSceneFromVerifiedPath(filePath, sceneSourceGuid, AssetImportRequest::RequestingApplication::AssetProcessor);
if (!scene)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load the requested scene.");
return nullptr;
}
return scene;
}
} // namespace SceneBuilder
@@ -0,0 +1,40 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneCore/Events/SceneSerializationBus.h>
namespace SceneBuilder
{
class SceneSerializationHandler
: public AZ::Component
, public AZ::SceneAPI::Events::SceneSerializationBus::Handler
{
public:
AZ_COMPONENT(SceneSerializationHandler, "{5917845E-2A6A-4C6C-BD02-E9CECC8D4E13}", AZ::Component);
SceneSerializationHandler() = default;
~SceneSerializationHandler() override = default;
void Activate() override;
void Deactivate() override;
static void Reflect(AZ::ReflectContext* context);
AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> LoadScene(
const AZStd::string& sceneFilePath, AZ::Uuid sceneSourceGuid) override;
};
} // namespace SceneBuilder
@@ -0,0 +1,50 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <SceneBuilder/TraceMessageHook.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
namespace SceneBuilder
{
TraceMessageHook::TraceMessageHook()
{
BusConnect();
}
TraceMessageHook::~TraceMessageHook()
{
BusDisconnect();
}
bool TraceMessageHook::OnPrintf(const char* window, [[maybe_unused]] const char* message)
{
if (AzFramework::StringFunc::Equal(window, AZ::SceneAPI::Utilities::ErrorWindow))
{
AZ_Error(window, false, "%s", message);
AssetBuilderSDK::AssetBuilderTraceBus::Broadcast(&AssetBuilderSDK::AssetBuilderTraceBus::Events::IgnoreNextPrintf, 1);
return true;
}
else if (AzFramework::StringFunc::Equal(window, AZ::SceneAPI::Utilities::WarningWindow))
{
AZ_Warning(window, false, "%s", message);
AssetBuilderSDK::AssetBuilderTraceBus::Broadcast(&AssetBuilderSDK::AssetBuilderTraceBus::Events::IgnoreNextPrintf, 1);
return true;
}
else
{
return false;
}
}
} // namespace SceneBuilder
@@ -0,0 +1,28 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Debug/TraceMessageBus.h>
namespace SceneBuilder
{
class TraceMessageHook
: public AZ::Debug::TraceMessageBus::Handler
{
public:
TraceMessageHook();
~TraceMessageHook() override;
bool OnPrintf(const char* window, const char* message) override;
};
} // namespace SceneBuilder
@@ -0,0 +1,122 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Module/Module.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzFramework/Metrics/MetricsPlainTextNameRegistration.h>
#include <SceneBuilder/SceneBuilderComponent.h>
#include <SceneBuilder/SceneSerializationHandler.h>
#include <Config/Components/SceneProcessingConfigSystemComponent.h>
#include <Config/Components/SoftNameBehavior.h>
#include <Config/Widgets/GraphTypeSelector.h>
#include <Exporting/Components/TangentGenerateComponent.h>
#include <Exporting/Components/TangentPreExportComponent.h>
#include <Source/SceneProcessingModule.h>
namespace AZ
{
namespace SceneProcessing
{
class SceneProcessingModule
: public Module
{
public:
AZ_RTTI(SceneProcessingModule, "{13DCFEF2-BB25-4DBB-A69B-22958CAD6885}", Module);
SceneProcessingModule()
: Module()
{
LoadSceneModule(s_sceneCoreModule, "SceneCore");
LoadSceneModule(s_sceneDataModule, "SceneData");
LoadSceneModule(s_fbxSceneBuilderModule, "FbxSceneBuilder");
m_descriptors.insert(m_descriptors.end(),
{
SceneProcessingConfig::SceneProcessingConfigSystemComponent::CreateDescriptor(),
SceneProcessingConfig::SoftNameBehavior::CreateDescriptor(),
SceneBuilder::BuilderPluginComponent::CreateDescriptor(),
SceneBuilder::SceneSerializationHandler::CreateDescriptor(),
AZ::SceneExportingComponents::TangentPreExportComponent::CreateDescriptor(),
AZ::SceneExportingComponents::TangentGenerateComponent::CreateDescriptor()
});
// This is an internal Amazon gem, so register it's components for metrics tracking, otherwise the name of the component won't get sent back.
// IF YOU ARE A THIRDPARTY WRITING A GEM, DO NOT REGISTER YOUR COMPONENTS WITH EditorMetricsComponentRegistrationBus
AZStd::vector<AZ::Uuid> typeIds;
typeIds.reserve(m_descriptors.size());
for (AZ::ComponentDescriptor* descriptor : m_descriptors)
{
typeIds.emplace_back(descriptor->GetUuid());
}
AzFramework::MetricsPlainTextNameRegistrationBus::Broadcast(&AzFramework::MetricsPlainTextNameRegistrationBus::Events::RegisterForNameSending, typeIds);
}
~SceneProcessingModule()
{
UnloadModule(s_fbxSceneBuilderModule);
UnloadModule(s_sceneDataModule);
UnloadModule(s_sceneCoreModule);
}
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList
{
azrtti_typeid<SceneProcessingConfig::SceneProcessingConfigSystemComponent>(),
};
}
void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SceneConfiguration", 0x2a3785fb));
}
void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("SceneConfiguration", 0x2a3785fb));
}
protected:
void LoadSceneModule(AZStd::unique_ptr<DynamicModuleHandle>& module, const char* name)
{
if (!module)
{
module = DynamicModuleHandle::Create(name);
if (module)
{
module->Load(false);
auto init = module->GetFunction<InitializeDynamicModuleFunction>(InitializeDynamicModuleFunctionName);
if (init)
{
(*init)(AZ::Environment::GetInstance());
}
}
}
}
void UnloadModule(AZStd::unique_ptr<DynamicModuleHandle>& module)
{
if (module)
{
auto uninit = module->GetFunction<UninitializeDynamicModuleFunction>(UninitializeDynamicModuleFunctionName);
if (uninit)
{
(*uninit)();
}
module.reset();
}
}
};
} // namespace SceneProcessing
} // namespace AZ
AZ_DECLARE_MODULE_CLASS(Gem_SceneProcessing, AZ::SceneProcessing::SceneProcessingModule)
@@ -0,0 +1,21 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Module/DynamicModuleHandle.h>
namespace AZ::SceneProcessing
{
inline AZStd::unique_ptr<DynamicModuleHandle> s_sceneCoreModule;
inline AZStd::unique_ptr<DynamicModuleHandle> s_sceneDataModule;
inline AZStd::unique_ptr<DynamicModuleHandle> s_fbxSceneBuilderModule;
} // namespace AZ::SceneProcessing
@@ -0,0 +1,36 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if !defined(SCENE_PROCESSING_EDITOR)
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Module/Module.h>
namespace AZ
{
namespace SceneProcessing
{
class SceneProcessingModuleStub
: public Module
{
public:
AZ_RTTI(SceneProcessingModuleStub, "{23438D63-EA7F-425B-82F2-5B45C072B4E5}", Module);
SceneProcessingModuleStub()
: Module()
{
}
};
} // namespace SceneProcessing
} // namespace AZ
AZ_DECLARE_MODULE_CLASS(Gem_SceneProcessing, AZ::SceneProcessing::SceneProcessingModuleStub)
#endif