Merge branch 'stabilization/2106' into non-uniform-scale-migration

This commit is contained in:
greerdv
2021-06-21 22:29:35 +01:00
18 changed files with 370 additions and 75 deletions
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Helpers Icon</title>
<defs>
<circle id="path-1" cx="8" cy="8" r="8"></circle>
<path d="M8.59736328,10.1821289 L8.59736328,9.79394531 C8.59736328,9.07617188 8.86835937,8.71728516 9.84248047,8.15332031 C10.8751953,7.54541016 11.4098633,6.77636719 11.4098633,5.67041016 C11.4098633,4.00048828 10.0255859,2.8359375 7.93085937,2.8359375 C5.68232422,2.8359375 4.40791016,4.09570312 4.37128906,5.89746094 L6.35615234,5.89746094 C6.40009766,5.06982422 6.95673828,4.53515625 7.79902344,4.53515625 C8.63398437,4.53515625 9.190625,5.04052734 9.190625,5.73632812 C9.190625,6.43212891 8.90498047,6.79101562 7.96015625,7.35498047 C6.94941406,7.94824219 6.54658203,8.60742188 6.64179687,9.75732422 L6.65644531,10.1821289 L8.59736328,10.1821289 Z M7.70380859,13.7124023 C8.52412109,13.7124023 9.02949219,13.2436523 9.02949219,12.4892578 C9.02949219,11.7275391 8.52412109,11.2587891 7.70380859,11.2587891 C6.88349609,11.2587891 6.37080078,11.7275391 6.37080078,12.4892578 C6.37080078,13.2436523 6.88349609,13.7124023 7.70380859,13.7124023 Z" id="path-3"></path>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="2nd-ToolBar-v2-(Perspective)-3" transform="translate(-1161.000000, -7.000000)">
<g id="2nd-ToolBar-buttons-on-right" transform="translate(1076.000000, 5.000000)">
<g id="Helpers-Icon" transform="translate(85.000000, 2.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Oval" fill="#FFFFFF" xlink:href="#path-1"></use>
<g id="?" fill-rule="nonzero" mask="url(#mask-2)">
<use fill="#909090" xlink:href="#path-3"></use>
<use fill="#3F3F3F" xlink:href="#path-3"></use>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -29,5 +29,6 @@
<file alias="trackview_editor.svg">Menu/trackview_editor.svg</file>
<file alias="ui_editor.svg">Menu/ui_editor.svg</file>
<file alias="menu.svg">Menu/menu.svg</file>
<file alias="helpers.svg">Menu/helpers.svg</file>
</qresource>
</RCC>
+9 -7
View File
@@ -130,6 +130,7 @@ CViewportTitleDlg::CViewportTitleDlg(QWidget* pParent)
SetupCameraDropdownMenu();
SetupResolutionDropdownMenu();
SetupViewportInformationMenu();
SetupHelpersButton();
SetupOverflowMenu();
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, gSettings.bMuteAudio ? m_oMuteAudioRequest : m_oUnmuteAudioRequest);
@@ -211,15 +212,16 @@ void CViewportTitleDlg::SetupViewportInformationMenu()
}
void CViewportTitleDlg::SetupHelpersButton()
{
connect(m_ui->m_helpers, &QToolButton::clicked, this, &CViewportTitleDlg::OnToggleHelpers);
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
}
void CViewportTitleDlg::SetupOverflowMenu()
{
// Setup the overflow menu
QMenu* overFlowMenu = new QMenu(this);
m_debugHelpersAction = new QAction("Debug Helpers", overFlowMenu);
m_debugHelpersAction->setCheckable(true);
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
connect(m_debugHelpersAction, &QAction::triggered, this, &CViewportTitleDlg::OnToggleHelpers);
overFlowMenu->addAction(m_debugHelpersAction);
m_audioMuteAction = new QAction("Mute Audio", overFlowMenu);
connect(m_audioMuteAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedMuteAudio);
@@ -333,7 +335,7 @@ void CViewportTitleDlg::OnMaximize()
void CViewportTitleDlg::OnToggleHelpers()
{
Helpers::ToggleHelpers();
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
}
void CViewportTitleDlg::SetNoViewportInfo()
@@ -759,7 +761,7 @@ void CViewportTitleDlg::OnEditorNotifyEvent(EEditorNotifyEvent event)
switch (event)
{
case eNotify_OnDisplayRenderUpdate:
m_debugHelpersAction->setChecked(Helpers::IsHelpersShown());
m_ui->m_helpers->setChecked(Helpers::IsHelpersShown());
break;
case eNotify_OnBeginGameMode:
case eNotify_OnEndGameMode:
+1 -1
View File
@@ -102,6 +102,7 @@ protected:
void SetupResolutionDropdownMenu();
void SetupViewportInformationMenu();
void SetupOverflowMenu();
void SetupHelpersButton();
QString m_title;
@@ -172,7 +173,6 @@ protected:
QAction* m_normalInformationAction = nullptr;
QAction* m_fullInformationAction = nullptr;
QAction* m_compactInformationAction = nullptr;
QAction* m_debugHelpersAction = nullptr;
QAction* m_audioMuteAction = nullptr;
QAction* m_enableVRAction = nullptr;
QAction* m_enableGridSnappingAction = nullptr;
+12
View File
@@ -81,6 +81,18 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_helpers">
<property name="icon">
<iconset>
<normaloff>:/Menu/helpers.svg</normaloff>:/Menu/helpers.svg
</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_resolutionMenu">
<property name="icon">
@@ -255,7 +255,6 @@ namespace AZ
{
return AZStd::make_pair(animation, anim);
}
Events::ProcessingResult AssImpAnimationImporter::ImportAnimation(AssImpSceneNodeAppendedContext& context)
{
AZ_TraceContext("Importer", "Animation");
@@ -447,7 +446,22 @@ namespace AZ
return combinedAnimationResult.GetResult();
}
decltype(boneAnimations) parentFillerAnimations;
AZStd::unordered_set<AZStd::string> boneList;
for (int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
{
aiMesh* mesh = scene->mMeshes[meshIndex];
for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
{
aiBone* bone = mesh->mBones[boneIndex];
boneList.insert(bone->mName.C_Str());
}
}
decltype(boneAnimations) fillerAnimations;
// Go through all the animations and make sure we create animations for bones who's parents don't have an animation
for (auto&& anim : boneAnimations)
@@ -459,8 +473,8 @@ namespace AZ
{
if (!IsPivotNode(parent->mName))
{
if (boneAnimations.find(parent->mName.C_Str()) == boneAnimations.end() &&
parentFillerAnimations.find(parent->mName.C_Str()) == parentFillerAnimations.end())
if (!boneAnimations.contains(parent->mName.C_Str()) &&
!fillerAnimations.contains(parent->mName.C_Str()))
{
// Create 1 key for each type that just copies the current transform
ConsolidatedNodeAnim emptyAnimation;
@@ -472,7 +486,7 @@ namespace AZ
globalTransform.Decompose(scale, rotation, position);
emptyAnimation.mNumRotationKeys = emptyAnimation.mNumPositionKeys = emptyAnimation.mNumScalingKeys = 1;
emptyAnimation.m_ownedPositionKeys.emplace_back(0, position);
emptyAnimation.mPositionKeys = emptyAnimation.m_ownedPositionKeys.data();
@@ -481,9 +495,9 @@ namespace AZ
emptyAnimation.m_ownedScalingKeys.emplace_back(0, scale);
emptyAnimation.mScalingKeys = emptyAnimation.m_ownedScalingKeys.data();
parentFillerAnimations.insert(
AZStd::make_pair(parent->mName.C_Str(), AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
fillerAnimations.insert(AZStd::make_pair(
parent->mName.C_Str(), AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
}
}
@@ -491,7 +505,7 @@ namespace AZ
}
}
boneAnimations.insert(AZStd::make_move_iterator(parentFillerAnimations.begin()), AZStd::make_move_iterator(parentFillerAnimations.end()));
boneAnimations.insert(AZStd::make_move_iterator(fillerAnimations.begin()), AZStd::make_move_iterator(fillerAnimations.end()));
auto animItr = boneAnimations.equal_range(currentNode->mName.C_Str());
@@ -98,6 +98,20 @@ namespace AZ
}
}
aiMatrix4x4 CalculateWorldTransform(const aiNode* currentNode)
{
aiMatrix4x4 transform = {};
const aiNode* iteratingNode = currentNode;
while (iteratingNode)
{
transform = iteratingNode->mTransformation * transform;
iteratingNode = iteratingNode->mParent;
}
return transform;
}
Events::ProcessingResult AssImpBoneImporter::ImportBone(AssImpNodeEncounteredContext& context)
{
AZ_TraceContext("Importer", "Bone");
@@ -111,12 +125,7 @@ namespace AZ
}
bool isBone = false;
if (NodeParentIsOfType(context.m_scene.GetGraph(), context.m_currentGraphPosition, DataTypes::IBoneData::TYPEINFO_Uuid()))
{
isBone = true;
}
else
{
AZStd::unordered_map<AZStd::string, const aiNode*> mainBoneList;
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
@@ -170,15 +179,8 @@ namespace AZ
{
createdBoneData = AZStd::make_shared<SceneData::GraphData::RootBoneData>();
}
aiMatrix4x4 transform = currentNode->mTransformation;
const aiNode* parent = currentNode->mParent;
while (parent)
{
transform = parent->mTransformation * transform;
parent = parent->mParent;
}
aiMatrix4x4 transform = CalculateWorldTransform(currentNode);
SceneAPI::DataTypes::MatrixType globalTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(transform);
@@ -31,7 +31,7 @@ namespace AZ
~AssImpBoneImporter() override = default;
static void Reflect(ReflectContext* context);
Events::ProcessingResult ImportBone(AssImpNodeEncounteredContext& context);
};
} // namespace FbxSceneBuilder
@@ -46,8 +46,9 @@ namespace AZ
serializeContext->Class<AssImpTransformImporter, SceneCore::LoadingComponent>()->Version(1);
}
}
void GetAllBones(const aiScene* scene, AZStd::unordered_map<AZStd::string, const aiBone*>& boneLookup)
void GetAllBones(
const aiScene* scene, AZStd::unordered_multimap<AZStd::string, const aiBone*>& boneLookup)
{
for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
{
@@ -57,7 +58,7 @@ namespace AZ
{
const aiBone* bone = mesh->mBones[boneIndex];
boneLookup[bone->mName.C_Str()] = bone;
boneLookup.emplace(bone->mName.C_Str(), bone);
}
}
}
@@ -73,41 +74,53 @@ namespace AZ
return Events::ProcessingResult::Ignored;
}
AZStd::unordered_map<AZStd::string, const aiBone*> boneLookup;
AZStd::unordered_multimap<AZStd::string, const aiBone*> boneLookup;
GetAllBones(scene, boneLookup);
auto boneIterator = boneLookup.find(currentNode->mName.C_Str());
const bool isBone = boneIterator != boneLookup.end();
aiMatrix4x4 combinedTransform;
DataTypes::MatrixType localTransform;
if (isBone)
{
auto parentNode = currentNode->mParent;
AZStd::vector<DataTypes::MatrixType> offsets, inverseOffsets;
auto iteratingNode = currentNode;
aiMatrix4x4 offsetMatrix = boneIterator->second->mOffsetMatrix;
aiMatrix4x4 parentOffset {};
auto parentBoneIterator = boneLookup.find(parentNode->mName.C_Str());
if (parentNode && parentBoneIterator != boneLookup.end())
while (iteratingNode && boneLookup.count(iteratingNode->mName.C_Str()))
{
const auto& parentBone = parentBoneIterator->second;
AZStd::string name = iteratingNode->mName.C_Str();
parentOffset = parentBone->mOffsetMatrix;
auto range = boneLookup.equal_range(name);
if (range.first != range.second)
{
// There can be multiple offsetMatrices for a given bone, we're only interested in grabbing the first one
auto boneFirstOffsetMatrix = range.first->second->mOffsetMatrix;
auto azMat = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(boneFirstOffsetMatrix);
offsets.push_back(azMat);
inverseOffsets.push_back(azMat.GetInverseFull());
}
iteratingNode = iteratingNode->mParent;
}
auto inverseOffset = offsetMatrix;
inverseOffset.Inverse();
combinedTransform = parentOffset * inverseOffset;
localTransform =
offsets.at(AZ::GetMin(offsets.size()-1, static_cast<decltype(offsets.size())>(1))) // parent bone offset, or if there is no parent, then current node offset
* inverseOffsets.at(inverseOffsets.size() - 1) // Inverse of root bone offset
* offsets.at(offsets.size() - 1) // Root bone offset
* inverseOffsets.at(0); // Inverse of current node offset
}
else
{
combinedTransform = GetConcatenatedLocalTransform(currentNode);
localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(GetConcatenatedLocalTransform(currentNode));
}
DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
// Don't bother adding a node with the identity matrix
if (localTransform == DataTypes::MatrixType::Identity())
{
return Events::ProcessingResult::Ignored;
}
context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
context.m_sourceSceneSystem.ConvertUnit(localTransform);
@@ -173,7 +173,7 @@ namespace AZ
{
if (inputPropertyValue.IsObject() && inputPropertyValue.HasMember("Value") && inputPropertyValue.HasMember("$type"))
{
// Requiring explicit type info to differentiate be=tween colors versus vectors and numeric types
// Requiring explicit type info to differentiate between colors versus vectors and numeric types
const AZ::Uuid baseTypeId = azrtti_typeid<T>();
AZ::Uuid typeId = AZ::Uuid::CreateNull();
result.Combine(LoadTypeId(typeId, inputPropertyValue, context, &baseTypeId));
@@ -198,7 +198,7 @@ namespace AZ
{
outputPropertyValue.SetObject();
// Storing explicit type info to differentiate be=tween colors versus vectors and numeric types
// Storing explicit type info to differentiate between colors versus vectors and numeric types
rapidjson::Value typeValue;
result.Combine(StoreTypeId(typeValue, azrtti_typeid<T>(), context));
outputPropertyValue.AddMember("$type", typeValue, context.GetJsonAllocator());
@@ -14,6 +14,7 @@
// AZ
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/map.h>
// Graph Model
@@ -14,10 +14,10 @@
// AZ
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/any.h>
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
// Graph Model
#include <GraphModel/Integration/GraphCanvasMetadata.h>
#include <GraphModel/Model/Common.h>
#include <GraphModel/Model/GraphElement.h>
@@ -136,9 +136,9 @@ namespace GraphModel
//! Set/gets a bundle of generic metadata that is provided by the node graph UI
//! system. This may include node positions, comment blocks, node groupings, and
//! bookmarks, for example.
void SetUiMetadata(const AZStd::any& uiMetadata);
const AZStd::any& GetUiMetadata() const;
AZStd::any& GetUiMetadata();
void SetUiMetadata(const GraphModelIntegration::GraphCanvasMetadata& uiMetadata);
const GraphModelIntegration::GraphCanvasMetadata& GetUiMetadata() const;
GraphModelIntegration::GraphCanvasMetadata& GetUiMetadata();
AZStd::shared_ptr<Slot> FindSlot(const Endpoint& endpoint);
@@ -157,7 +157,7 @@ namespace GraphModel
ConnectionList m_connections;
//! Used to store and serialize metadata from the graph UI, like node positions, comments, group boxes, etc.
AZStd::any m_uiMetadata;
GraphModelIntegration::GraphCanvasMetadata m_uiMetadata;
//! Used to store all of our node <-> wrapper node mappings
NodeWrappingMap m_nodeWrappings;
@@ -12,6 +12,7 @@
#pragma once
// AZ
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/std/any.h>
#include <AzCore/std/hash.h>
#include <AzCore/std/containers/list.h>
@@ -165,6 +166,32 @@ namespace GraphModel
ExtendableSlotConfiguration m_extendableSlotConfiguration;
};
//! Custom JSON serializer for Slot because we use an AZStd::any for m_value
class JsonSlotSerializer
: public AZ::BaseJsonSerializer
{
public:
AZ_RTTI(JsonSlotSerializer, "{8AC96D70-7BCD-4D68-8813-269938982D51}", AZ::BaseJsonSerializer);
AZ_CLASS_ALLOCATOR(JsonSlotSerializer, AZ::SystemAllocator, 0);
AZ::JsonSerializationResult::Result Load(
void* outputValue, const AZ::Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
AZ::JsonDeserializerContext& context) override;
AZ::JsonSerializationResult::Result Store(
rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const AZ::Uuid& valueTypeId,
AZ::JsonSerializerContext& context) override;
private:
template<typename T>
bool LoadAny(
AZStd::any& propertyValue, const rapidjson::Value& inputPropertyValue, AZ::JsonDeserializerContext& context,
AZ::JsonSerializationResult::ResultCode& result);
template<typename T>
bool StoreAny(
const AZStd::any& propertyValue, rapidjson::Value& outputPropertyValue, AZ::JsonSerializerContext& context,
AZ::JsonSerializationResult::ResultCode& result);
};
//!!! Start in Graph.h for high level GraphModel documentation !!!
@@ -180,6 +207,7 @@ namespace GraphModel
class Slot : public GraphElement, public AZStd::enable_shared_from_this<Slot>
{
friend class Graph; // So the Graph can update the Slot's cache of Connection pointers
friend class JsonSlotSerializer; // So we can set the m_value and m_subId directly from the serializer
public:
AZ_CLASS_ALLOCATOR(Slot, AZ::SystemAllocator, 0);
@@ -1338,11 +1338,7 @@ namespace GraphModelIntegration
GraphCanvasMetadata* GraphController::GetGraphMetadata()
{
if (!m_graph->GetUiMetadata().is<GraphCanvasMetadata>())
{
m_graph->SetUiMetadata(AZStd::any(GraphCanvasMetadata()));
}
GraphCanvasMetadata* graphCanvasMetadata = AZStd::any_cast<GraphCanvasMetadata>(&m_graph->GetUiMetadata());
GraphCanvasMetadata* graphCanvasMetadata = &m_graph->GetUiMetadata();
AZ_Assert(graphCanvasMetadata, "GraphCanvasMetadata not initialized");
return graphCanvasMetadata;
}
+4 -4
View File
@@ -37,7 +37,7 @@ namespace GraphModel
if (serializeContext)
{
serializeContext->Class<Graph>()
->Version(1)
->Version(2)
->Field("m_nodes", &Graph::m_nodes)
->Field("m_connections", &Graph::m_connections)
->Field("m_uiMetadata", &Graph::m_uiMetadata)
@@ -312,19 +312,19 @@ namespace GraphModel
}
void Graph::SetUiMetadata(const AZStd::any& uiMetadata)
void Graph::SetUiMetadata(const GraphModelIntegration::GraphCanvasMetadata& uiMetadata)
{
m_uiMetadata = uiMetadata;
}
const AZStd::any& Graph::GetUiMetadata() const
const GraphModelIntegration::GraphCanvasMetadata& Graph::GetUiMetadata() const
{
return m_uiMetadata;
}
AZStd::any& Graph::GetUiMetadata()
GraphModelIntegration::GraphCanvasMetadata& Graph::GetUiMetadata()
{
return m_uiMetadata;
}
+159 -3
View File
@@ -11,9 +11,14 @@
*/
// AZ
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/SerializeContext.h>
// Graph Model
@@ -294,13 +299,164 @@ namespace GraphModel
/////////////////////////////////////////////////////////
// Slot
AZ::JsonSerializationResult::Result JsonSlotSerializer::Load(
void* outputValue, const AZ::Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
AZ::JsonDeserializerContext& context)
{
namespace JSR = AZ::JsonSerializationResult;
AZ_Assert(
azrtti_typeid<Slot>() == outputValueTypeId,
"Unable to deserialize Slot from json because the provided type is %s.",
outputValueTypeId.ToString<AZStd::string>().c_str());
Slot* slot = reinterpret_cast<Slot*>(outputValue);
AZ_Assert(slot, "Output value for JsonSlotSerializer can't be null.");
JSR::ResultCode result(JSR::Tasks::ReadField);
auto serializedSlotValue = inputValue.FindMember("m_value");
if (serializedSlotValue != inputValue.MemberEnd())
{
AZStd::any slotValue;
if (LoadAny<bool>(slotValue, serializedSlotValue->value, context, result) ||
LoadAny<int>(slotValue, serializedSlotValue->value, context, result) ||
LoadAny<float>(slotValue, serializedSlotValue->value, context, result) ||
LoadAny<AZStd::string>(slotValue, serializedSlotValue->value, context, result) ||
LoadAny<AZ::Vector2>(slotValue, serializedSlotValue->value, context, result) ||
LoadAny<AZ::Vector3>(slotValue, serializedSlotValue->value, context, result) ||
LoadAny<AZ::Vector4>(slotValue, serializedSlotValue->value, context, result) ||
LoadAny<AZ::EntityId>(slotValue, serializedSlotValue->value, context, result))
{
slot->m_value = slotValue;
}
}
// Load m_subId normally because it's just an int
{
SlotSubId slotSubId = 0;
result.Combine(ContinueLoadingFromJsonObjectField(
&slotSubId, azrtti_typeid<SlotSubId>(), inputValue,
"m_subId", context));
slot->m_subId = slotSubId;
}
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Succesfully loaded Slot information."
: "Failed to load Slot information.");
}
AZ::JsonSerializationResult::Result JsonSlotSerializer::Store(
rapidjson::Value& outputValue, const void* inputValue, [[maybe_unused]] const void* defaultValue, const AZ::Uuid& valueTypeId,
AZ::JsonSerializerContext& context)
{
namespace JSR = AZ::JsonSerializationResult;
AZ_Assert(
azrtti_typeid<Slot>() == valueTypeId,
"Unable to Serialize Slot because the provided type is %s.", valueTypeId.ToString<AZStd::string>().c_str());
const Slot* slot = reinterpret_cast<const Slot*>(inputValue);
AZ_Assert(slot, "Input value for JsonSlotSerializer can't be null.");
outputValue.SetObject();
JSR::ResultCode result(JSR::Tasks::WriteValue);
{
AZ::ScopedContextPath subPathPropertyOverrides(context, "m_value");
if (!slot->m_value.empty())
{
rapidjson::Value outputPropertyValue;
if (StoreAny<bool>(slot->m_value, outputPropertyValue, context, result) ||
StoreAny<int>(slot->m_value, outputPropertyValue, context, result) ||
StoreAny<float>(slot->m_value, outputPropertyValue, context, result) ||
StoreAny<AZStd::string>(slot->m_value, outputPropertyValue, context, result) ||
StoreAny<AZ::Vector2>(slot->m_value, outputPropertyValue, context, result) ||
StoreAny<AZ::Vector3>(slot->m_value, outputPropertyValue, context, result) ||
StoreAny<AZ::Vector4>(slot->m_value, outputPropertyValue, context, result) ||
StoreAny<AZ::EntityId>(slot->m_value, outputPropertyValue, context, result))
{
outputValue.AddMember("m_value", outputPropertyValue, context.GetJsonAllocator());
}
}
}
{
AZ::ScopedContextPath subSlotId(context, "m_subId");
SlotSubId defaultSubId = 0;
result.Combine(ContinueStoringToJsonObjectField(
outputValue, "m_subId", &slot->m_subId, &defaultSubId,
azrtti_typeid<SlotSubId>(), context));
}
return context.Report(
result,
result.GetProcessing() != JSR::Processing::Halted ? "Successfully stored MaterialAssignment information."
: "Failed to store MaterialAssignment information.");
}
template<typename T>
bool JsonSlotSerializer::LoadAny(
AZStd::any& propertyValue, const rapidjson::Value& inputPropertyValue, AZ::JsonDeserializerContext& context,
AZ::JsonSerializationResult::ResultCode& result)
{
auto valueItr = inputPropertyValue.FindMember("Value");
auto typeItr = inputPropertyValue.FindMember("$type");
if ((valueItr != inputPropertyValue.MemberEnd()) && (typeItr != inputPropertyValue.MemberEnd()))
{
// Requiring explicit type info to differentiate between colors versus vectors and numeric types
const AZ::Uuid baseTypeId = azrtti_typeid<T>();
AZ::Uuid typeId = AZ::Uuid::CreateNull();
result.Combine(LoadTypeId(typeId, inputPropertyValue, context, &baseTypeId));
if (typeId == azrtti_typeid<T>())
{
T value;
result.Combine(ContinueLoadingFromJsonObjectField(&value, azrtti_typeid<T>(), inputPropertyValue, "Value", context));
propertyValue = value;
return true;
}
}
return false;
}
template<typename T>
bool JsonSlotSerializer::StoreAny(
const AZStd::any& propertyValue, rapidjson::Value& outputPropertyValue, AZ::JsonSerializerContext& context,
AZ::JsonSerializationResult::ResultCode& result)
{
if (propertyValue.is<T>())
{
outputPropertyValue.SetObject();
// Storing explicit type info to differentiate between colors versus vectors and numeric types
rapidjson::Value typeValue;
result.Combine(StoreTypeId(typeValue, azrtti_typeid<T>(), context));
outputPropertyValue.AddMember("$type", typeValue, context.GetJsonAllocator());
T value = AZStd::any_cast<T>(propertyValue);
result.Combine(
ContinueStoringToJsonObjectField(outputPropertyValue, "Value", &value, nullptr, azrtti_typeid<T>(), context));
return true;
}
return false;
}
void Slot::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
if (auto jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
{
jsonContext->Serializer<JsonSlotSerializer>()->HandlesType<Slot>();
}
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Slot>()
->Version(0)
->Version(1)
->Field("m_value", &Slot::m_value)
->Field("m_subId", &Slot::m_subId)
// m_slotDescription is not reflected because that data is populated procedurally by each node
@@ -460,10 +460,10 @@ namespace LandscapeCanvasEditor
GraphCanvas::StyleManagerRequestBus::Event(editorId, &GraphCanvas::StyleManagerRequests::RegisterDataPaletteStyle, LandscapeCanvas::AreaTypeId, "VegetationAreaDataColorPalette");
LandscapeCanvas::LandscapeCanvasRequestBus::Handler::BusConnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
AzToolsFramework::EntityCompositionNotificationBus::Handler::BusConnect();
AzToolsFramework::ToolsApplicationNotificationBus::Handler::BusConnect();
AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusConnect();
CrySystemEventBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
@@ -480,6 +480,7 @@ namespace LandscapeCanvasEditor
{
AZ::EntitySystemBus::Handler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler::BusDisconnect();
AzToolsFramework::ToolsApplicationNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
@@ -883,6 +884,10 @@ namespace LandscapeCanvasEditor
if (landscapeCanvasComponent)
{
landscapeCanvasComponent->m_graph = *m_serializeContext->CloneObject(graph.get());
// Mark the Landscape Canvas entity as dirty so the changes to the graph will be picked up on the next save
AzToolsFramework::ScopedUndoBatch undo("Update Landscape Canvas Graph");
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, rootEntityId);
}
}
}
@@ -1572,7 +1577,7 @@ namespace LandscapeCanvasEditor
void MainWindow::HandleEditorEntityCreated(const AZ::EntityId& entityId, GraphCanvas::GraphId graphId)
{
if (m_ignoreGraphUpdates)
if (m_ignoreGraphUpdates || m_prefabPropagationInProgress)
{
return;
}
@@ -1622,6 +1627,11 @@ namespace LandscapeCanvasEditor
void MainWindow::OnEditorEntityDeleted(const AZ::EntityId& entityId)
{
if (m_prefabPropagationInProgress)
{
return;
}
m_queuedEntityDeletes.push_back(entityId);
QTimer::singleShot(0, [this, entityId]() {
@@ -2456,6 +2466,11 @@ namespace LandscapeCanvasEditor
void MainWindow::EntityParentChanged(AZ::EntityId entityId, AZ::EntityId newParentId, AZ::EntityId oldParentId)
{
if (m_prefabPropagationInProgress)
{
return;
}
GraphCanvas::GraphId oldGraphId = FindGraphContainingEntity(oldParentId);
GraphCanvas::GraphId newGraphId = FindGraphContainingEntity(newParentId);
@@ -2482,6 +2497,20 @@ namespace LandscapeCanvasEditor
}
}
void MainWindow::OnPrefabInstancePropagationBegin()
{
// Ignore graph updates during prefab propagation because the entities will be
// deleted and re-created, which would inadvertantly trigger our logic to close
// the graph when the corresponding entity is deleted.
m_prefabPropagationInProgress = true;
}
void MainWindow::OnPrefabInstancePropagationEnd()
{
// See comment above in OnPrefabInstancePropagationBegin
m_prefabPropagationInProgress = false;
}
void MainWindow::OnCryEditorEndCreate()
{
UpdateGraphEnabled();
@@ -2490,6 +2519,15 @@ namespace LandscapeCanvasEditor
void MainWindow::OnCryEditorEndLoad()
{
UpdateGraphEnabled();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
}
void MainWindow::OnCryEditorCloseScene()
{
UpdateGraphEnabled();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
}
void MainWindow::OnCryEditorSceneClosed()
@@ -24,6 +24,7 @@
#include <AzToolsFramework/API/EntityCompositionNotificationBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
@@ -84,6 +85,7 @@ namespace LandscapeCanvasEditor
, private AzToolsFramework::EntityCompositionNotificationBus::Handler
, private AzToolsFramework::PropertyEditorEntityChangeNotificationBus::MultiHandler
, private AzToolsFramework::ToolsApplicationNotificationBus::Handler
, private AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler
, private CrySystemEventBus::Handler
{
Q_OBJECT
@@ -183,10 +185,15 @@ namespace LandscapeCanvasEditor
void EntityParentChanged(AZ::EntityId entityId, AZ::EntityId newParentId, AZ::EntityId oldParentId) override;
////////////////////////////////////////////////////////////////////////
//! PrefabPublicNotificationBus overrides
void OnPrefabInstancePropagationBegin() override;
void OnPrefabInstancePropagationEnd() override;
////////////////////////////////////////////////////////////////////////
// CrySystemEventBus overrides
void OnCryEditorEndCreate() override;
void OnCryEditorEndLoad() override;
void OnCryEditorCloseScene() override;
void OnCryEditorSceneClosed() override;
////////////////////////////////////////////////////////////////////////
@@ -246,6 +253,7 @@ namespace LandscapeCanvasEditor
AZ::SerializeContext* m_serializeContext = nullptr;
bool m_ignoreGraphUpdates = false;
bool m_prefabPropagationInProgress = false;
bool m_inObjectPickMode = false;
using DeletedNodePositionsMap = AZStd::unordered_map<AZ::EntityComponentIdPair, AZ::Vector2>;