d34d088191
This has a small bundle of bugfixes and improvements all based around improving prefab TrackView support: * JsonMerger - improved the error message when patch remove operations fail to make the specific failure more obvious Instance - swapped the order of destroying entities vs clearing the lookup tables so that lookups still produce valid results during destruction. (This could happen while creating undo caches) * InstanceEntityIdMapper - in the case where an id isn't found, it now returns an invalid id instead of an "attempted-valid" one that still generally turned out to be not-valid * PrefabUndo - downgraded a potential crash to an error message if for some reason the patch contains changes to an entity that doesn't currently have an alias. (This case can be caused occasionally by other bugs and error conditions) * EditorSequenceComponent - downgraded a potential crash to an assert for the times when it tries to remove components, fails, but thinks it succeeded. (This case can currently be caused by using Maestro with Prefabs enabled) * EditorSequenceAgentComponent - added an undo cache refresh whenever the component deletes itself, so that deleting itself during an EditorSequenceComponent destruction chain of events leaves the undo cache in the correct state. * SliceConverter - fixed the conversion of entity references in top-level slice instance entities that refer down to nested slice entities. There was a chicken-and-egg problem in terms of which entities need to be created first to make the references and the prefab patching & serialization happy. This was worked around by creating placeholder top-level entities, then the nested slice entities, then replacing the top-level entities with the fully-realized ones. Specific changes: * Added more informative error message. Signed-off-by: mbalfour <mbalfour@amazon.com> (cherry picked from commit 672608a6c833c07295996cd9b3449825222b74d0) * Changed the error condition to produce a "valid" invalid id instead of a deterministic but not-valid id Signed-off-by: mbalfour <mbalfour@amazon.com> (cherry picked from commit 3673950c949de8e067b32ddafaffd07e648a13d8) * Guard against invalid reference assert/crash Signed-off-by: mbalfour <mbalfour@amazon.com> (cherry picked from commit 268d4ef3447f268a1372d07e028b9e67bac5c64e) * Downgrade an invalid reference crash to an assert Signed-off-by: mbalfour <mbalfour@amazon.com> (cherry picked from commit 38c9303770845f4e863273dd6fb8fc7e83380425) * Improved logic for handling entity references across nested slices. Signed-off-by: mbalfour <mbalfour@amazon.com> (cherry picked from commit 7e89a016d95fb72cb5f119e1e3768daa60e6bfb4) * Changed order of entities.clear() call so that instance lookups are still valid during entity destruction. Signed-off-by: mbalfour <mbalfour@amazon.com> * Add undo cache notification when removing Maestro components. Signed-off-by: mbalfour <mbalfour@amazon.com>
305 lines
15 KiB
C++
305 lines
15 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
#include "Maestro_precompiled.h"
|
|
#include "EditorSequenceAgentComponent.h"
|
|
#include "SequenceAgentComponent.h"
|
|
|
|
#include <AzCore/RTTI/BehaviorContext.h>
|
|
#include <AzCore/std/containers/set.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/Serialization/EditContext.h>
|
|
#include <AzCore/Component/ComponentApplicationBus.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzFramework/API/ApplicationAPI.h>
|
|
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
|
|
#include <AzCore/Component/Entity.h>
|
|
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
|
|
#include <Maestro/Types/AnimParamType.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.h>
|
|
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
|
|
|
|
namespace Maestro
|
|
{
|
|
void EditorSequenceAgentComponent::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<EditorSequenceAgentComponent, EditorComponentBase>()
|
|
->Field("SequenceComponentEntityIds", &EditorSequenceAgentComponent::m_sequenceEntityIds)
|
|
->Version(3);
|
|
|
|
AZ::EditContext* editContext = serializeContext->GetEditContext();
|
|
|
|
if (editContext)
|
|
{
|
|
editContext->Class<EditorSequenceAgentComponent>(
|
|
"SequenceAgent", "Maps Director Component Animations to Behavior Properties on this Entity")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::Category, "Cinematics")
|
|
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/SequenceAgent.png")
|
|
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/SequenceAgent.png")
|
|
->Attribute(AZ::Edit::Attributes::AddableByUser, false) // SequenceAgents are only added by TrackView
|
|
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
const AZ::Uuid& EditorSequenceAgentComponent::GetComponentTypeUuid(const AZ::Component& component) const
|
|
{
|
|
return AzToolsFramework::GetUnderlyingComponentType(component);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::GetEntityComponents(AZ::Entity::ComponentArrayType& entityComponents) const
|
|
{
|
|
AZ::Entity* entity = GetEntity();
|
|
AZ_Assert(entity, "Expected valid entity.");
|
|
if (entity)
|
|
{
|
|
// Add all enabled components
|
|
const AZ::Entity::ComponentArrayType& enabledComponents = entity->GetComponents();
|
|
for (AZ::Component* component : enabledComponents)
|
|
{
|
|
entityComponents.push_back(component);
|
|
}
|
|
|
|
// Add all disabled components
|
|
AZ::Entity::ComponentArrayType disabledComponents;
|
|
AzToolsFramework::EditorDisabledCompositionRequestBus::Event(entity->GetId(), &AzToolsFramework::EditorDisabledCompositionRequests::GetDisabledComponents, disabledComponents);
|
|
|
|
for (AZ::Component* component : disabledComponents)
|
|
{
|
|
entityComponents.push_back(component);
|
|
}
|
|
|
|
// Add all pending components
|
|
AZ::Entity::ComponentArrayType pendingComponents;
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(entity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
|
|
for (AZ::Component* component : pendingComponents)
|
|
{
|
|
entityComponents.push_back(component);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::Activate()
|
|
{
|
|
// cache pointers and animatable addresses for animation
|
|
//
|
|
CacheAllVirtualPropertiesFromBehaviorContext();
|
|
|
|
ConnectAllSequences();
|
|
|
|
EditorComponentBase::Activate();
|
|
|
|
// Notify the sequence agent was just connected to the sequence.
|
|
Maestro::EditorSequenceAgentComponentNotificationBus::Event(
|
|
GetEntityId(),
|
|
&Maestro::EditorSequenceAgentComponentNotificationBus::Events::OnSequenceAgentConnected
|
|
);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::Deactivate()
|
|
{
|
|
// invalidate all cached pointers and address
|
|
m_addressToBehaviorVirtualPropertiesMap.clear();
|
|
|
|
DisconnectAllSequences();
|
|
|
|
EditorComponentBase::Deactivate();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::ConnectSequence(const AZ::EntityId& sequenceEntityId)
|
|
{
|
|
// check that we aren't already connected to this SequenceComponent - add it if we aren't
|
|
if (m_sequenceEntityIds.find(sequenceEntityId) == m_sequenceEntityIds.end())
|
|
{
|
|
m_sequenceEntityIds.insert(sequenceEntityId);
|
|
// connect to EBus between the given SequenceComponent and me
|
|
Maestro::SequenceAgentEventBusId busId(sequenceEntityId, GetEntityId());
|
|
EditorSequenceAgentComponentRequestBus::MultiHandler::BusConnect(busId);
|
|
SequenceAgentComponentRequestBus::MultiHandler::BusConnect(busId);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::DisconnectSequence()
|
|
{
|
|
const Maestro::SequenceAgentEventBusId* busIdToDisconnect = SequenceAgentComponentRequestBus::GetCurrentBusId();
|
|
|
|
if (!busIdToDisconnect)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ::EntityId sequenceEntityId = busIdToDisconnect->first;
|
|
|
|
// we only process DisconnectSequence events sent over an ID'ed bus - otherwise we don't know which SequenceComponent to disconnect
|
|
auto findIter = m_sequenceEntityIds.find(sequenceEntityId);
|
|
AZ_Assert(findIter != m_sequenceEntityIds.end(), "A sequence not connected to SequenceAgentComponent on %s is requesting a disconnection", GetEntity()->GetName().c_str());
|
|
|
|
m_sequenceEntityIds.erase(sequenceEntityId);
|
|
|
|
// Disconnect from the bus between the SequenceComponent and me
|
|
// Make a copy because calling BusDisconnect destroy the current bus id
|
|
const Maestro::SequenceAgentEventBusId busIdToDisconnectCopy = *busIdToDisconnect;
|
|
EditorSequenceAgentComponentRequestBus::MultiHandler::BusDisconnect(busIdToDisconnectCopy);
|
|
SequenceAgentComponentRequestBus::MultiHandler::BusDisconnect(busIdToDisconnectCopy);
|
|
|
|
if (m_sequenceEntityIds.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ::EntityId curEntityId = GetEntityId();
|
|
|
|
// remove this SequenceAgent from this entity if no sequenceComponents are connected to it
|
|
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&AzToolsFramework::EntityCompositionRequests::RemoveComponents, AZ::Entity::ComponentArrayType{this});
|
|
|
|
// Let any currently-active undo operations know that this entity has changed state.
|
|
auto undoCacheInterface = AZ::Interface<AzToolsFramework::UndoSystem::UndoCacheInterface>::Get();
|
|
if (undoCacheInterface)
|
|
{
|
|
undoCacheInterface->UpdateCache(curEntityId);
|
|
}
|
|
|
|
// CAUTION!
|
|
// THIS CLASS INSTANCE IS NOW DEAD DUE TO DELETION BY THE ENTITY DURING RemoveComponents!
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::ConnectAllSequences()
|
|
{
|
|
// Connect all buses
|
|
for (auto iter = m_sequenceEntityIds.begin(); iter != m_sequenceEntityIds.end(); iter++)
|
|
{
|
|
Maestro::SequenceAgentEventBusId busIdToConnect(*iter, GetEntityId());
|
|
EditorSequenceAgentComponentRequestBus::MultiHandler::BusConnect(busIdToConnect);
|
|
SequenceAgentComponentRequestBus::MultiHandler::BusConnect(busIdToConnect);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::DisconnectAllSequences()
|
|
{
|
|
// disconnect all buses
|
|
for (auto iter = m_sequenceEntityIds.begin(); iter != m_sequenceEntityIds.end(); iter++)
|
|
{
|
|
Maestro::SequenceAgentEventBusId busIdToDisconnect(*iter, GetEntityId());
|
|
EditorSequenceAgentComponentRequestBus::MultiHandler::BusDisconnect(busIdToDisconnect);
|
|
SequenceAgentComponentRequestBus::MultiHandler::BusDisconnect(busIdToDisconnect);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::BuildGameEntity(AZ::Entity* gameEntity)
|
|
{
|
|
SequenceAgentComponent *sequenceAgentComponent = gameEntity->CreateComponent<SequenceAgentComponent>();
|
|
if (sequenceAgentComponent)
|
|
{
|
|
// TODO: when we have code which only allows animation of properties that are common between the SequenceAgent and EditorSequenceAgent,
|
|
// transfer the mappings to the game behaviors here
|
|
sequenceAgentComponent->m_sequenceEntityIds = m_sequenceEntityIds;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::GetAllAnimatableProperties(IAnimNode::AnimParamInfos& properties, AZ::ComponentId componentId)
|
|
{
|
|
// add all properties found during Activate() that match the given componentId
|
|
for (auto propertyIter = m_addressToBehaviorVirtualPropertiesMap.begin(); propertyIter != m_addressToBehaviorVirtualPropertiesMap.end(); propertyIter++)
|
|
{
|
|
if (propertyIter->first.GetComponentId() == componentId)
|
|
{
|
|
AZ::BehaviorEBus::VirtualProperty* virtualProperty = propertyIter->second;
|
|
// all behavior properties as string params with the virtual property name as the string
|
|
IAnimNode::SParamInfo paramInfo;
|
|
|
|
// by default set up paramType as an AnimParamType::ByString with the name as the Virtual Property name
|
|
paramInfo.paramType = propertyIter->first.GetVirtualPropertyName();
|
|
|
|
// check for paramType specialization attributes on the getter method of the virtual property. if found, reset
|
|
// to the eAnimParamType enum - this leaves the paramType name unchanged but changes the type.
|
|
for (int i = virtualProperty->m_getter->m_attributes.size(); --i >= 0;)
|
|
{
|
|
if (virtualProperty->m_getter->m_attributes[i].first == AZ::Edit::Attributes::PropertyPosition)
|
|
{
|
|
paramInfo.paramType = AnimParamType::Position;
|
|
break;
|
|
}
|
|
else if (virtualProperty->m_getter->m_attributes[i].first == AZ::Edit::Attributes::PropertyRotation)
|
|
{
|
|
paramInfo.paramType = AnimParamType::Rotation;
|
|
break;
|
|
}
|
|
else if (virtualProperty->m_getter->m_attributes[i].first == AZ::Edit::Attributes::PropertyScale)
|
|
{
|
|
paramInfo.paramType = AnimParamType::Scale;
|
|
break;
|
|
}
|
|
else if (virtualProperty->m_getter->m_attributes[i].first == AZ::Edit::Attributes::PropertyHidden)
|
|
{
|
|
paramInfo.flags = static_cast<IAnimNode::ESupportedParamFlags>(paramInfo.flags | IAnimNode::eSupportedParamFlags_Hidden);
|
|
break;
|
|
}
|
|
}
|
|
|
|
properties.push_back(paramInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::GetAnimatableComponents(AZStd::vector<AZ::ComponentId>& animatableComponentIds)
|
|
{
|
|
AZStd::set<AZ::ComponentId> appendedComponentIds;
|
|
|
|
// go through all known properties found during Activate() - insert all unique components found
|
|
for (auto address = m_addressToBehaviorVirtualPropertiesMap.begin(); address != m_addressToBehaviorVirtualPropertiesMap.end(); address++)
|
|
{
|
|
// only append component if it's not already been appended
|
|
auto findIter = appendedComponentIds.find(address->first.GetComponentId());
|
|
if (findIter == appendedComponentIds.end())
|
|
{
|
|
animatableComponentIds.push_back(address->first.GetComponentId());
|
|
appendedComponentIds.insert(address->first.GetComponentId());
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AZ::Uuid EditorSequenceAgentComponent::GetAnimatedAddressTypeId(const AnimatablePropertyAddress& animatableAddress)
|
|
{
|
|
return GetVirtualPropertyTypeId(animatableAddress);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::GetAnimatedPropertyValue(AnimatedValue& returnValue, const AnimatablePropertyAddress& animatableAddress)
|
|
{
|
|
SequenceAgent::GetAnimatedPropertyValue(returnValue, GetEntityId(), animatableAddress);
|
|
}
|
|
|
|
bool EditorSequenceAgentComponent::SetAnimatedPropertyValue(const AnimatablePropertyAddress& animatableAddress, const AnimatedValue& value)
|
|
{
|
|
return SequenceAgent::SetAnimatedPropertyValue(GetEntityId(), animatableAddress, value);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void EditorSequenceAgentComponent::GetAssetDuration(AnimatedValue& returnValue, AZ::ComponentId componentId, const AZ::Data::AssetId& assetId)
|
|
{
|
|
SequenceAgent::GetAssetDuration(returnValue, componentId, assetId);
|
|
}
|
|
|
|
} // namespace Maestro
|