Merge pull request #997 from aws-lumberyard-dev/Spawnable/ScriptCanvas/Integration

Add dynamic spawn node to Script Canvas
This commit is contained in:
sconel
2021-05-28 13:09:25 -07:00
committed by GitHub
16 changed files with 385 additions and 6 deletions
@@ -123,6 +123,7 @@ namespace AZ
const static AZ::Crc32 NameLabelOverride = AZ_CRC("NameLabelOverride", 0x9ff79cab);
const static AZ::Crc32 AssetPickerTitle = AZ_CRC_CE("AssetPickerTitle");
const static AZ::Crc32 HideProductFilesInAssetPicker = AZ_CRC_CE("HideProductFilesInAssetPicker");
const static AZ::Crc32 ChildNameLabelOverride = AZ_CRC("ChildNameLabelOverride", 0x73dd2909);
//! Container attribute that is used to override labels for its elements given the index of the element
const static AZ::Crc32 IndexedChildNameLabelOverride = AZ_CRC("IndexedChildNameLabelOverride", 0x5f313ac2);
@@ -10,6 +10,7 @@
*
*/
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Spawnable/Spawnable.h>
@@ -88,4 +89,10 @@ namespace AzFramework
{
extensions.push_back(Spawnable::FileExtension);
}
uint32_t SpawnableAssetHandler::BuildSubId(AZStd::string_view id)
{
AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size());
return azlossy_caster(subIdHash.GetHash());
}
} // namespace AzFramework
@@ -47,6 +47,7 @@ namespace AzFramework
const char* GetGroup() const override;
const char* GetBrowserIcon() const override;
void GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) override;
static uint32_t BuildSubId(AZStd::string_view id);
protected:
LoadResult LoadAssetData(
@@ -10,7 +10,7 @@
*
*/
#include <AzCore/Casting/lossy_cast.h>
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
#include <AzToolsFramework/Prefab/Spawnable/ProcesedObjectStore.h>
namespace AzToolsFramework::Prefab::PrefabConversionUtils
@@ -73,8 +73,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
uint32_t ProcessedObjectStore::BuildSubId(AZStd::string_view id)
{
AZ::Uuid subIdHash = AZ::Uuid::CreateData(id.data(), id.size());
return azlossy_caster(subIdHash.GetHash());
return AzFramework::SpawnableAssetHandler::BuildSubId(id);
}
const AZStd::string& ProcessedObjectStore::GetId() const
@@ -777,6 +777,23 @@ namespace AzToolsFramework
selection.SetDefaultDirectory(defaultDirectory);
}
if (m_hideProductFilesInAssetPicker)
{
FilterConstType displayFilter = selection.GetDisplayFilter();
EntryTypeFilter* productsFilter = new EntryTypeFilter();
productsFilter->SetEntryType(AssetBrowserEntry::AssetEntryType::Product);
InverseFilter* noProductsFilter = new InverseFilter();
noProductsFilter->SetFilter(FilterConstType(productsFilter));
CompositeFilter* compFilter = new CompositeFilter(CompositeFilter::LogicOperatorType::AND);
compFilter->AddFilter(FilterConstType(displayFilter));
compFilter->AddFilter(FilterConstType(noProductsFilter));
selection.SetDisplayFilter(FilterConstType(compFilter));
}
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, parentWidget());
if (selection.IsValid())
{
@@ -1172,6 +1189,16 @@ namespace AzToolsFramework
return m_showProductAssetName;
}
void PropertyAssetCtrl::SetHideProductFilesInAssetPicker(bool hide)
{
m_hideProductFilesInAssetPicker = hide;
}
bool PropertyAssetCtrl::GetHideProductFilesInAssetPicker() const
{
return m_hideProductFilesInAssetPicker;
}
void PropertyAssetCtrl::SetShowThumbnail(bool enable)
{
m_showThumbnail = enable;
@@ -1297,6 +1324,14 @@ namespace AzToolsFramework
GUI->SetShowProductAssetName(showProductAssetName);
}
}
else if(attrib == AZ::Edit::Attributes::HideProductFilesInAssetPicker)
{
bool hideProductFilesInAssetPicker = false;
if (attrValue->Read<bool>(hideProductFilesInAssetPicker))
{
GUI->SetHideProductFilesInAssetPicker(hideProductFilesInAssetPicker);
}
}
else if (attrib == AZ::Edit::Attributes::ClearNotify)
{
PropertyAssetCtrl::ClearCallbackType* func = azdynamic_cast<PropertyAssetCtrl::ClearCallbackType*>(attrValue->GetAttribute());
@@ -158,6 +158,10 @@ namespace AzToolsFramework
//! Assets can be either source or product assets generated from source assets. By default, source assets are shown in the property asset. You can override that with this flag.
bool m_showProductAssetName = true;
//! Assets can be either source or product assets generated from source assets.
//! By default the asset picker shows both on an AZ::Asset<> property. You can hide product assets with this flag.
bool m_hideProductFilesInAssetPicker = false;
bool m_showThumbnail = false;
bool m_showThumbnailDropDownButton = false;
EditCallbackType* m_thumbnailCallback = nullptr;
@@ -211,6 +215,9 @@ namespace AzToolsFramework
void SetShowProductAssetName(bool enable);
bool GetShowProductAssetName() const;
void SetHideProductFilesInAssetPicker(bool hide);
bool GetHideProductFilesInAssetPicker() const;
void SetShowThumbnail(bool enable);
bool GetShowThumbnail() const;
void SetShowThumbnailDropDownButton(bool enable);
@@ -59,6 +59,7 @@ namespace ScriptCanvasBuilder
QuantumLeap,
DependencyArguments,
DependencyRequirementsData,
AddAssetDependencySearch,
// add new entries above
Current,
};
@@ -681,6 +681,19 @@ namespace ScriptCanvasBuilder
}
AssetBuilderSDK::JobProduct jobProduct;
// Scan our runtime input for any asset references
// Store them as product dependencies
AssetBuilderSDK::OutputObject(&runtimeData.m_input,
azrtti_typeid<decltype(runtimeData.m_input)>(),
input.runtimeScriptCanvasOutputPath,
azrtti_typeid<ScriptCanvas::RuntimeAsset>(),
AZ_CRC("RuntimeData", 0x163310ae), jobProduct);
// Output Object marks dependencies as handled.
// We still have more to evaluate
jobProduct.m_dependenciesHandled = false;
jobProduct.m_dependencies.push_back({ runtimeData.m_script.GetId(), {} });
for (const auto& assetDependency : runtimeData.m_requiredAssets)
@@ -713,9 +726,6 @@ namespace ScriptCanvasBuilder
}
jobProduct.m_dependenciesHandled = true;
jobProduct.m_productFileName = input.runtimeScriptCanvasOutputPath;
jobProduct.m_productAssetType = azrtti_typeid<ScriptCanvas::RuntimeAsset>();
jobProduct.m_productSubID = AZ_CRC("RuntimeData", 0x163310ae);
input.response->m_outputProducts.push_back(AZStd::move(jobProduct));
return AZ::Success();
}
@@ -16,6 +16,7 @@
#include <Libraries/Logic/Logic.h>
#include <Libraries/Math/Math.h>
#include <Libraries/Comparison/Comparison.h>
#include <Libraries/Spawning/Spawning.h>
#include <Libraries/UnitTesting/UnitTestingLibrary.h>
#include <AzCore/std/containers/vector.h>
@@ -33,6 +34,7 @@ namespace ScriptCanvas
Entity::InitNodeRegistry(*g_nodeRegistry);
Comparison::InitNodeRegistry(*g_nodeRegistry);
Time::InitNodeRegistry(*g_nodeRegistry);
Spawning::InitNodeRegistry(*g_nodeRegistry);
String::InitNodeRegistry(*g_nodeRegistry);
Operators::InitNodeRegistry(*g_nodeRegistry);
@@ -61,6 +63,7 @@ namespace ScriptCanvas
Entity::Reflect(reflectContext);
Comparison::Reflect(reflectContext);
Time::Reflect(reflectContext);
Spawning::Reflect(reflectContext);
String::Reflect(reflectContext);
Operators::Reflect(reflectContext);
@@ -90,6 +93,9 @@ namespace ScriptCanvas
componentDescriptors = Time::GetComponentDescriptors();
libraryDescriptors.insert(libraryDescriptors.end(), componentDescriptors.begin(), componentDescriptors.end());
componentDescriptors = Spawning::GetComponentDescriptors();
libraryDescriptors.insert(libraryDescriptors.end(), componentDescriptors.begin(), componentDescriptors.end());
componentDescriptors = String::GetComponentDescriptors();
libraryDescriptors.insert(libraryDescriptors.end(), componentDescriptors.begin(), componentDescriptors.end());
@@ -143,6 +143,17 @@ namespace ScriptCanvas
};
struct Spawning : public LibraryDefinition
{
AZ_RTTI(Spawning, "{41E910AE-FBD2-41AD-9173-5105141F0466}", LibraryDefinition);
static void Reflect(AZ::ReflectContext*);
static void InitNodeRegistry(NodeRegistry& nodeRegistry);
static AZStd::vector<AZ::ComponentDescriptor*> GetComponentDescriptors();
~Spawning() override = default;
};
struct String : public LibraryDefinition
{
AZ_RTTI(String, "{5B700838-21A2-4579-9303-F4A4822AFEF4}", LibraryDefinition);
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<ScriptCanvas Include="Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Class Name="SpawnNodeable"
QualifiedName="Nodeables::Spawning::SpawnNodeable"
PreferredClassName="Spawn"
Base="ScriptCanvas::Nodeable"
Icon="Icons/ScriptCanvas/Placeholder.png"
Category="Spawning"
Version="0"
GeneratePropertyFriend="True"
Namespace="ScriptCanvas"
Description="Spawns a selected prefab, positioned using the provided transform inputs">
<Input Name="Request Spawn" OutputName="Spawn Requested">
<Parameter Name="Translation" Type="Data::Vector3Type" Description="Position to spawn"/>
<Parameter Name="Rotation" Type="Data::Vector3Type" Description="Rotation of spawn (in degrees)"/>
<Parameter Name="Scale" Type="Data::NumberType" DefaultValue="1" Description="Scale of spawn"/>
</Input>
<Output Name="On Spawn">
<Parameter Name="SpawnedEntitiesList" Type="AZStd::vector&lt;Data::EntityIDType&gt;" Description="List of spawned entities sorted by hierarchy with the root being first"/>
</Output>/>
<Property Name ="m_spawnableAsset" Type="AZ::Data::Asset&lt;AzFramework::Spawnable&gt;" Serialize="true">
<PropertyData Name="Prefab Asset"
Description="Prefab source asset to spawn."
Serialize="true"
UIHandler="AZ::Edit::UIHandlers::Default">
<EditAttribute Key="AZ::Edit::Attributes::ShowProductAssetFileName" Value="false"/>
<EditAttribute Key="AZ::Edit::Attributes::HideProductFilesInAssetPicker" Value="true"/>
<EditAttribute Key="AZ::Edit::Attributes::AssetPickerTitle" Value="&quot;a Prefab&quot;"/>
<EditAttribute Key="AZ::Edit::Attributes::ChangeNotify" Value="&amp;SpawnNodeable::OnSpawnAssetChanged"/>
</PropertyData>
</Property>
</Class>
</ScriptCanvas>
@@ -0,0 +1,139 @@
/*
* 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 <ScriptCanvas/Libraries/Spawning/SpawnNodeable.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
namespace ScriptCanvas::Nodeables::Spawning
{
SpawnNodeable::SpawnNodeable(const SpawnNodeable& rhs)
: m_spawnableAsset(rhs.m_spawnableAsset)
{}
SpawnNodeable& SpawnNodeable::operator=(SpawnNodeable& rhs)
{
m_spawnableAsset = rhs.m_spawnableAsset;
return *this;
}
void SpawnNodeable::OnInitializeExecutionState()
{
if (!AZ::TickBus::Handler::BusIsConnected())
{
AZ::TickBus::Handler::BusConnect();
}
m_spawnTicket = AzFramework::EntitySpawnTicket(m_spawnableAsset);
}
void SpawnNodeable::OnDeactivate()
{
AZ::TickBus::Handler::BusDisconnect();
m_spawnTicket = AzFramework::EntitySpawnTicket();
}
void SpawnNodeable::OnTick([[maybe_unused]] float delta, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
{
AZStd::vector<Data::EntityIDType> swappedSpawnedEntityList;
AZStd::vector<size_t> swappedSpawnBatchSizes;
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_idBatchMutex);
swappedSpawnedEntityList.swap(m_spawnedEntityList);
swappedSpawnBatchSizes.swap(m_spawnBatchSizes);
}
AZ::EntityId* batchBegin = swappedSpawnedEntityList.data();
for (size_t batchSize : swappedSpawnBatchSizes)
{
if (batchSize == 0)
{
continue;
}
AZStd::vector<AZ::EntityId> spawnedEntitiesBatch(
batchBegin, batchBegin + batchSize);
CallOnSpawn(AZStd::move(spawnedEntitiesBatch));
batchBegin += batchSize;
}
}
void SpawnNodeable::OnSpawnAssetChanged()
{
if (m_spawnableAsset.GetId().IsValid())
{
AZStd::string rootSpawnableFile;
AzFramework::StringFunc::Path::GetFileName(m_spawnableAsset.GetHint().c_str(), rootSpawnableFile);
rootSpawnableFile += AzFramework::Spawnable::DotFileExtension;
AZ::u32 rootSubId = AzFramework::SpawnableAssetHandler::BuildSubId(AZStd::move(rootSpawnableFile));
if (m_spawnableAsset.GetId().m_subId != rootSubId)
{
AZ::Data::AssetId rootAssetId = m_spawnableAsset.GetId();
rootAssetId.m_subId = rootSubId;
m_spawnableAsset = AZ::Data::AssetManager::Instance().
FindOrCreateAsset<AzFramework::Spawnable>(rootAssetId, AZ::Data::AssetLoadBehavior::Default);
}
else
{
m_spawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::Default);
}
}
}
void SpawnNodeable::RequestSpawn(Data::Vector3Type translation, Data::Vector3Type rotation, Data::NumberType scale)
{
if (m_spawnableAsset.GetAutoLoadBehavior() == AZ::Data::AssetLoadBehavior::NoLoad)
{
return;
}
auto preSpawnCB = [this, translation, rotation, scale]([[maybe_unused]] AzFramework::EntitySpawnTicket& ticket,
AzFramework::SpawnableEntityContainerView view)
{
AZ::Entity* rootEntity = *view.begin();
AzFramework::TransformComponent* entityTransform =
rootEntity->FindComponent<AzFramework::TransformComponent>();
if (entityTransform)
{
AZ::Vector3 rotationCopy = rotation;
AZ::Quaternion rotationQuat = AZ::Quaternion::CreateFromEulerAnglesDegrees(rotationCopy);
entityTransform->SetWorldTM(AZ::Transform(translation, rotationQuat, AZ::Vector3(scale, scale, scale)));
}
};
auto spawnCompleteCB = [this]([[maybe_unused]] AzFramework::EntitySpawnTicket& ticket,
AzFramework::SpawnableConstEntityContainerView view)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_idBatchMutex);
m_spawnedEntityList.reserve(m_spawnedEntityList.size() + view.size());
for (const AZ::Entity* entity : view)
{
m_spawnedEntityList.emplace_back(entity->GetId());
}
m_spawnBatchSizes.push_back(view.size());
};
AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(m_spawnTicket, preSpawnCB, spawnCompleteCB);
}
}
@@ -0,0 +1,52 @@
/*
* 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 <Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.generated.h>
#include <AzCore/Component/TickBus.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include <ScriptCanvas/CodeGen/NodeableCodegen.h>
#include <ScriptCanvas/Core/Node.h>
#include <ScriptCanvas/Core/Nodeable.h>
namespace ScriptCanvas::Nodeables::Spawning
{
class SpawnNodeable
: public ScriptCanvas::Nodeable,
public AZ::TickBus::Handler
{
SCRIPTCANVAS_NODE(SpawnNodeable);
public:
SpawnNodeable() = default;
SpawnNodeable(const SpawnNodeable& rhs);
SpawnNodeable& operator=(SpawnNodeable& rhs);
void OnInitializeExecutionState() override;
void OnDeactivate() override;
//TickBus
void OnTick(float delta, AZ::ScriptTimePoint timePoint) override;
void OnSpawnAssetChanged();
private:
AzFramework::EntitySpawnTicket m_spawnTicket;
AZStd::vector<Data::EntityIDType> m_spawnedEntityList;
AZStd::vector<size_t> m_spawnBatchSizes;
AZStd::recursive_mutex m_idBatchMutex;
};
}
@@ -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 <ScriptCanvas/Libraries/Libraries.h>
#include <ScriptCanvas/Libraries/Spawning/Spawning.h>
namespace ScriptCanvas
{
namespace Library
{
void Spawning::Reflect(AZ::ReflectContext* reflection)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
{
serializeContext->Class<Spawning, LibraryDefinition>()
->Version(1)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<Spawning>("Spawning", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/ScriptCanvas/Libraries/Entity.png");
}
}
}
void Spawning::InitNodeRegistry(NodeRegistry& nodeRegistry)
{
AddNodeToRegistry<Spawning, ScriptCanvas::Nodes::SpawnNodeableNode>(nodeRegistry);
}
AZStd::vector<AZ::ComponentDescriptor*> Spawning::GetComponentDescriptors()
{
return AZStd::vector<AZ::ComponentDescriptor*>({
ScriptCanvas::Nodes::SpawnNodeableNode::CreateDescriptor(),
});
}
}
}
@@ -0,0 +1,17 @@
/*
* 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
// This header is only meant to include the nodes and should not contain
// shared code
#include <ScriptCanvas/Libraries/Spawning/SpawnNodeable.h>
@@ -454,6 +454,11 @@ set(FILES
Include/ScriptCanvas/Libraries/Time/TimerNodeable.h
Include/ScriptCanvas/Libraries/Time/TimerNodeable.cpp
Include/ScriptCanvas/Libraries/Time/TimerNodeable.ScriptCanvasNodeable.xml
Include/ScriptCanvas/Libraries/Spawning/Spawning.cpp
Include/ScriptCanvas/Libraries/Spawning/Spawning.h
Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.cpp
Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.h
Include/ScriptCanvas/Libraries/Spawning/SpawnNodeable.ScriptCanvasNodeable.xml
Include/ScriptCanvas/Libraries/String/Contains.cpp
Include/ScriptCanvas/Libraries/String/Contains.h
Include/ScriptCanvas/Libraries/String/Contains.ScriptCanvasGrammar.xml