/* * 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 "EMotionFX_precompiled.h" #include <AzCore/Component/ComponentApplication.h> #include <AzCore/Component/TransformBus.h> #include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/EditContext.h> #include <AzCore/RTTI/BehaviorContext.h> #include <AzFramework/Physics/CharacterBus.h> #include <LmbrCentral/Physics/CryCharacterPhysicsBus.h> #include <EMotionFX/Source/Allocators.h> #include <EMotionFX/Source/SingleThreadScheduler.h> #include <EMotionFX/Source/EMotionFXManager.h> #include <EMotionFX/Source/AnimGraphManager.h> #include <EMotionFX/Source/AnimGraphObjectFactory.h> #include <EMotionFX/Source/MotionSet.h> #include <EMotionFX/Source/Recorder.h> #include <EMotionFX/Source/ConstraintTransformRotationAngles.h> #include <EMotionFX/Source/Parameter/ParameterFactory.h> #include <EMotionFX/Source/TwoStringEventData.h> #include <EMotionFX/Source/EventDataFootIK.h> #include <EMotionFX/Source/PhysicsSetup.h> #include <EMotionFX/Source/SimulatedObjectSetup.h> #include <MCore/Source/Command.h> #include <EMotionFX/CommandSystem/Source/MotionEventCommands.h> #include <EMotionFX/CommandSystem/Source/SimulatedObjectCommands.h> #include <EMotionFX/Source/PoseData.h> #include <EMotionFX/Source/PoseDataRagdoll.h> #include <Integration/EMotionFXBus.h> #include <Integration/Assets/ActorAsset.h> #include <Integration/Assets/MotionAsset.h> #include <Integration/Assets/MotionSetAsset.h> #include <Integration/Assets/AnimGraphAsset.h> #include <Integration/Rendering/Cry/CryRenderBackend.h> #include <Integration/System/SystemComponent.h> #include <AzFramework/Physics/World.h> #include <Integration/MotionExtractionBus.h> #if defined(EMOTIONFXANIMATION_EDITOR) // EMFX tools / editor includes # include <IEditor.h> // Qt # include <QtGui/QSurfaceFormat> // EMStudio tools and main window registration # include <LyViewPaneNames.h> # include <AzToolsFramework/API/ViewPaneOptions.h> # include <AzCore/std/string/wildcard.h> # include <QApplication> # include <EMotionStudio/EMStudioSDK/Source/EMStudioManager.h> # include <EMotionStudio/EMStudioSDK/Source/MainWindow.h> # include <EMotionStudio/EMStudioSDK/Source/PluginManager.h> // EMStudio plugins # include <EMotionStudio/Plugins/StandardPlugins/Source/LogWindow/LogWindowPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/CommandBar/CommandBarPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/ActionHistory/ActionHistoryPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/MotionWindow/MotionWindowPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/MorphTargetsWindow/MorphTargetsWindowPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/TimeView/TimeViewPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/Attachments/AttachmentsPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/SceneManager/SceneManagerPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/NodeWindow/NodeWindowPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/MotionEvents/MotionEventsPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/MotionSetsWindow/MotionSetsWindowPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/NodeGroups/NodeGroupsPlugin.h> # include <EMotionStudio/Plugins/StandardPlugins/Source/AnimGraph/AnimGraphPlugin.h> # include <EMotionStudio/Plugins/RenderPlugins/Source/OpenGLRender/OpenGLRenderPlugin.h> # include <Editor/Plugins/HitDetection/HitDetectionJointInspectorPlugin.h> # include <Editor/Plugins/SkeletonOutliner/SkeletonOutlinerPlugin.h> # include <Editor/Plugins/Ragdoll/RagdollNodeInspectorPlugin.h> # include <Editor/Plugins/Cloth/ClothJointInspectorPlugin.h> # include <Editor/Plugins/SimulatedObject/SimulatedObjectWidget.h> # include <Source/Editor/PropertyWidgets/PropertyTypes.h> # include <EMotionFX_Traits_Platform.h> #endif // EMOTIONFXANIMATION_EDITOR #include <ISystem.h> // include required AzCore headers #include <AzCore/IO/FileIO.h> #include <AzFramework/API/ApplicationAPI.h> namespace EMotionFX { namespace Integration { ////////////////////////////////////////////////////////////////////////// class EMotionFXEventHandler : public EMotionFX::EventHandler { public: AZ_CLASS_ALLOCATOR(EMotionFXEventHandler, EMotionFXAllocator, 0); const AZStd::vector<EventTypes> GetHandledEventTypes() const { return { EVENT_TYPE_ON_EVENT, EVENT_TYPE_ON_HAS_LOOPED, EVENT_TYPE_ON_STATE_ENTERING, EVENT_TYPE_ON_STATE_ENTER, EVENT_TYPE_ON_STATE_END, EVENT_TYPE_ON_STATE_EXIT, EVENT_TYPE_ON_START_TRANSITION, EVENT_TYPE_ON_END_TRANSITION }; } /// Dispatch motion events to listeners via ActorNotificationBus::OnMotionEvent. void OnEvent(const EMotionFX::EventInfo& emfxInfo) override { const ActorInstance* actorInstance = emfxInfo.mActorInstance; if (actorInstance) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); // Fill engine-compatible structure to dispatch to game code. MotionEvent motionEvent; motionEvent.m_entityId = owningEntityId; motionEvent.m_actorInstance = emfxInfo.mActorInstance; motionEvent.m_motionInstance = emfxInfo.mMotionInstance; motionEvent.m_time = emfxInfo.mTimeValue; // TODO for (const auto& eventData : emfxInfo.mEvent->GetEventDatas()) { if (const EMotionFX::TwoStringEventData* twoStringEventData = azrtti_cast<const EMotionFX::TwoStringEventData*>(eventData.get())) { motionEvent.m_eventTypeName = twoStringEventData->GetSubject().c_str(); motionEvent.SetParameterString(twoStringEventData->GetParameters().c_str(), twoStringEventData->GetParameters().size()); break; } } motionEvent.m_globalWeight = emfxInfo.mGlobalWeight; motionEvent.m_localWeight = emfxInfo.mLocalWeight; motionEvent.m_isEventStart = emfxInfo.IsEventStart(); // Queue the event to flush on the main thread. ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnMotionEvent, AZStd::move(motionEvent)); } } void OnHasLooped(EMotionFX::MotionInstance* motionInstance) override { const ActorInstance* actorInstance = motionInstance->GetActorInstance(); if (actorInstance) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnMotionLoop, motionInstance->GetMotion()->GetName()); } } void OnStateEntering(EMotionFX::AnimGraphInstance* animGraphInstance, EMotionFX::AnimGraphNode* state) override { const ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); if (actorInstance && state) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnStateEntering, state->GetName()); } } void OnStateEnter(EMotionFX::AnimGraphInstance* animGraphInstance, EMotionFX::AnimGraphNode* state) override { const ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); if (actorInstance && state) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnStateEntered, state->GetName()); } } void OnStateEnd(EMotionFX::AnimGraphInstance* animGraphInstance, EMotionFX::AnimGraphNode* state) override { const ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); if (actorInstance && state) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnStateExiting, state->GetName()); } } void OnStateExit(EMotionFX::AnimGraphInstance* animGraphInstance, EMotionFX::AnimGraphNode* state) override { const ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); if (actorInstance && state) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnStateExited, state->GetName()); } } void OnStartTransition(EMotionFX::AnimGraphInstance* animGraphInstance, EMotionFX::AnimGraphStateTransition* transition) override { const ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); if (actorInstance) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); const char* sourceName = transition->GetSourceNode() ? transition->GetSourceNode()->GetName() : ""; const char* targetName = transition->GetTargetNode() ? transition->GetTargetNode()->GetName() : ""; ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnStateTransitionStart, sourceName, targetName); } } void OnEndTransition(EMotionFX::AnimGraphInstance* animGraphInstance, EMotionFX::AnimGraphStateTransition* transition) override { const ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); if (actorInstance) { const AZ::EntityId owningEntityId = actorInstance->GetEntityId(); const char* sourceName = transition->GetSourceNode() ? transition->GetSourceNode()->GetName() : ""; const char* targetName = transition->GetTargetNode() ? transition->GetTargetNode()->GetName() : ""; ActorNotificationBus::QueueEvent(owningEntityId, &ActorNotificationBus::Events::OnStateTransitionEnd, sourceName, targetName); } } }; ////////////////////////////////////////////////////////////////////////// class ActorNotificationBusHandler : public ActorNotificationBus::Handler , public AZ::BehaviorEBusHandler { public: AZ_EBUS_BEHAVIOR_BINDER(ActorNotificationBusHandler, "{D2CD62E7-5FCF-4DC2-85DF-C205D5AB1E8B}", AZ::SystemAllocator, OnMotionEvent, OnMotionLoop, OnStateEntering, OnStateEntered, OnStateExiting, OnStateExited, OnStateTransitionStart, OnStateTransitionEnd); void OnMotionEvent(MotionEvent motionEvent) override { Call(FN_OnMotionEvent, motionEvent); } void OnMotionLoop(const char* motionName) override { Call(FN_OnMotionLoop, motionName); } void OnStateEntering(const char* stateName) override { Call(FN_OnStateEntering, stateName); } void OnStateEntered(const char* stateName) override { Call(FN_OnStateEntered, stateName); } void OnStateExiting(const char* stateName) override { Call(FN_OnStateExiting, stateName); } void OnStateExited(const char* stateName) override { Call(FN_OnStateExited, stateName); } void OnStateTransitionStart(const char* fromState, const char* toState) override { Call(FN_OnStateTransitionStart, fromState, toState); } void OnStateTransitionEnd(const char* fromState, const char* toState) override { Call(FN_OnStateTransitionEnd, fromState, toState); } }; SystemComponent::~SystemComponent() = default; int SystemComponent::emfx_updateEnabled = 1; int SystemComponent::emfx_actorRenderEnabled = 1; void SystemComponent::ReflectEMotionFX(AZ::ReflectContext* context) { MCore::ReflectionSerializer::Reflect(context); MCore::StringIdPoolIndex::Reflect(context); EMotionFX::ConstraintTransformRotationAngles::Reflect(context); // Actor EMotionFX::PhysicsSetup::Reflect(context); EMotionFX::SimulatedObjectSetup::Reflect(context); EMotionFX::PoseData::Reflect(context); EMotionFX::PoseDataRagdoll::Reflect(context); // Motion set EMotionFX::MotionSet::Reflect(context); EMotionFX::MotionSet::MotionEntry::Reflect(context); // Base AnimGraph objects EMotionFX::AnimGraphObject::Reflect(context); EMotionFX::AnimGraph::Reflect(context); EMotionFX::AnimGraphNodeGroup::Reflect(context); EMotionFX::AnimGraphGameControllerSettings::Reflect(context); // Anim graph objects EMotionFX::AnimGraphObjectFactory::ReflectTypes(context); // Anim graph's parameters EMotionFX::ParameterFactory::ReflectParameterTypes(context); EMotionFX::MotionEventTable::Reflect(context); EMotionFX::MotionEventTrack::Reflect(context); EMotionFX::AnimGraphSyncTrack::Reflect(context); EMotionFX::Event::Reflect(context); EMotionFX::MotionEvent::Reflect(context); EMotionFX::EventData::Reflect(context); EMotionFX::EventDataSyncable::Reflect(context); EMotionFX::TwoStringEventData::Reflect(context); EMotionFX::EventDataFootIK::Reflect(context); EMotionFX::Recorder::Reflect(context); EMotionFX::KeyTrackLinearDynamic<AZ::Vector3>::Reflect(context); EMotionFX::KeyTrackLinearDynamic<AZ::Quaternion>::Reflect(context); EMotionFX::KeyFrame<AZ::Vector3>::Reflect(context); EMotionFX::KeyFrame<AZ::Quaternion>::Reflect(context); MCore::Command::Reflect(context); CommandSystem::MotionIdCommandMixin::Reflect(context); CommandSystem::CommandAdjustMotion::Reflect(context); CommandSystem::CommandClearMotionEvents::Reflect(context); CommandSystem::CommandCreateMotionEventTrack::Reflect(context); CommandSystem::CommandAdjustMotionEventTrack::Reflect(context); CommandSystem::CommandCreateMotionEvent::Reflect(context); CommandSystem::CommandAdjustMotionEvent::Reflect(context); EMotionFX::CommandAdjustSimulatedObject::Reflect(context); EMotionFX::CommandAdjustSimulatedJoint::Reflect(context); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::Reflect(AZ::ReflectContext* context) { ReflectEMotionFX(context); // Reflect component for serialization. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); if (serializeContext) { serializeContext->Class<SystemComponent, AZ::Component>() ->Version(1) ->Field("NumThreads", &SystemComponent::m_numThreads) ; serializeContext->Class<MotionEvent>() ->Version(1) ; if (AZ::EditContext* ec = serializeContext->GetEditContext()) { ec->Class<SystemComponent>("EMotion FX Animation", "Enables the EMotion FX animation solution") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &SystemComponent::m_numThreads, "Number of threads", "Number of threads used internally by EMotion FX") ; } } // Reflect system-level types and EBuses to behavior context. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context); if (behaviorContext) { behaviorContext->EBus<SystemRequestBus>("SystemRequestBus") ; behaviorContext->EBus<SystemNotificationBus>("SystemNotificationBus") ; // In order for a property to be displayed in ScriptCanvas. Both a setter and a getter are necessary(both must be non-null). // This is being worked on in dragon branch, once this is complete the dummy lambda functions can be removed. behaviorContext->Class<MotionEvent>("MotionEvent") ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::Preview) ->Property("entityId", BehaviorValueGetter(&MotionEvent::m_entityId), [](MotionEvent*, const AZ::EntityId&) {}) ->Property("parameter", BehaviorValueGetter(&MotionEvent::m_parameter), [](MotionEvent*, const char*) {}) ->Property("eventType", BehaviorValueGetter(&MotionEvent::m_eventType), [](MotionEvent*, const AZ::u32&) {}) ->Property("eventTypeName", BehaviorValueGetter(&MotionEvent::m_eventTypeName), [](MotionEvent*, const char*) {}) ->Property("time", BehaviorValueGetter(&MotionEvent::m_time), [](MotionEvent*, const float&) {}) ->Property("globalWeight", BehaviorValueGetter(&MotionEvent::m_globalWeight), [](MotionEvent*, const float&) {}) ->Property("localWeight", BehaviorValueGetter(&MotionEvent::m_localWeight), [](MotionEvent*, const float&) {}) ->Property("isEventStart", BehaviorValueGetter(&MotionEvent::m_isEventStart), [](MotionEvent*, const bool&) {}) ; behaviorContext->EBus<ActorNotificationBus>("ActorNotificationBus") ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::Preview) ->Handler<ActorNotificationBusHandler>() ->Event("OnMotionEvent", &ActorNotificationBus::Events::OnMotionEvent) ->Event("OnMotionLoop", &ActorNotificationBus::Events::OnMotionLoop) ->Event("OnStateEntering", &ActorNotificationBus::Events::OnStateEntering) ->Event("OnStateEntered", &ActorNotificationBus::Events::OnStateEntered) ->Event("OnStateExiting", &ActorNotificationBus::Events::OnStateExiting) ->Event("OnStateExited", &ActorNotificationBus::Events::OnStateExited) ->Event("OnStateTransitionStart", &ActorNotificationBus::Events::OnStateTransitionStart) ->Event("OnStateTransitionEnd", &ActorNotificationBus::Events::OnStateTransitionEnd) ; } } ////////////////////////////////////////////////////////////////////////// void SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC("EMotionFXAnimationService", 0x3f8a6369)); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC("EMotionFXAnimationService", 0x3f8a6369)); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { required.push_back(AZ_CRC("AssetDatabaseService", 0x3abf5601)); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { dependent.push_back(AZ_CRC("AssetCatalogService", 0xc68ffc57)); dependent.push_back(AZ_CRC("JobsService", 0xd5ab5a50)); } ////////////////////////////////////////////////////////////////////////// SystemComponent::SystemComponent() : m_numThreads(1) { } ////////////////////////////////////////////////////////////////////////// void SystemComponent::Init() { } ////////////////////////////////////////////////////////////////////////// void SystemComponent::Activate() { // Start EMotionFX allocator. AZ::AllocatorInstance<EMotionFXAllocator>::Create(); // Initialize MCore, which is EMotionFX's standard library of containers and systems. MCore::Initializer::InitSettings coreSettings; coreSettings.mMemAllocFunction = &EMotionFXAlloc; coreSettings.mMemReallocFunction = &EMotionFXRealloc; coreSettings.mMemFreeFunction = &EMotionFXFree; if (!MCore::Initializer::Init(&coreSettings)) { AZ_Error("EMotion FX Animation", false, "Failed to initialize EMotion FX SDK Core"); return; } // Initialize EMotionFX runtime. EMotionFX::Initializer::InitSettings emfxSettings; emfxSettings.mUnitType = MCore::Distance::UNITTYPE_METERS; if (!EMotionFX::Initializer::Init(&emfxSettings)) { AZ_Error("EMotion FX Animation", false, "Failed to initialize EMotion FX SDK Runtime"); return; } // Initialize media root to asset cache. // \todo We're in touch with the Systems team. Seems like the aliases are initialized pretty late and we can't access it when activating the system component. // Until this is resolved, we're going to init everything in the OnCrySystemInitialized(). SetMediaRoot("@assets@"); // Register EMotionFX event handler m_eventHandler.reset(aznew EMotionFXEventHandler()); EMotionFX::GetEventManager().AddEventHandler(m_eventHandler.get()); // Setup asset types. RegisterAssetTypesAndHandlers(); SystemRequestBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect(); CrySystemEventBus::Handler::BusConnect(); EMotionFXRequestBus::Handler::BusConnect(); EnableRayRequests(); m_renderBackendManager = AZStd::make_unique<RenderBackendManager>(); // Default to Cry render backend. The RenderBackendManager will manage the lifetime of the CryRenderBackend. CryRenderBackend* cryRenderBackend = aznew CryRenderBackend(); m_renderBackendManager->SetRenderBackend(cryRenderBackend); #if defined (EMOTIONFXANIMATION_EDITOR) AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); AzToolsFramework::EditorAnimationSystemRequestsBus::Handler::BusConnect(); AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect(); m_updateTimer.Stamp(); // Register custom property handlers for the reflected property editor. m_propertyHandlers = RegisterPropertyTypes(); #endif // EMOTIONFXANIMATION_EDITOR } ////////////////////////////////////////////////////////////////////////// void SystemComponent::Deactivate() { #if defined(EMOTIONFXANIMATION_EDITOR) // Unregister custom property handlers for the reflected property editor. UnregisterPropertyTypes(m_propertyHandlers); m_propertyHandlers.clear(); if (EMStudio::GetManager()) { EMStudio::Initializer::Shutdown(); MysticQt::Initializer::Shutdown(); } { using namespace AzToolsFramework; EditorRequests::Bus::Broadcast(&EditorRequests::UnregisterViewPane, EMStudio::MainWindow::GetEMotionFXPaneName()); } AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorAnimationSystemRequestsBus::Handler::BusDisconnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); #endif // EMOTIONFXANIMATION_EDITOR m_renderBackendManager.reset(); EMotionFX::GetEventManager().RemoveEventHandler(m_eventHandler.get()); m_eventHandler.reset(); AZ::TickBus::Handler::BusDisconnect(); CrySystemEventBus::Handler::BusDisconnect(); EMotionFXRequestBus::Handler::BusDisconnect(); DisableRayRequests(); if (SystemRequestBus::Handler::BusIsConnected()) { SystemRequestBus::Handler::BusDisconnect(); m_assetHandlers.resize(0); EMotionFX::Initializer::Shutdown(); MCore::Initializer::Shutdown(); } // Memory leaks will be reported. AZ::AllocatorInstance<EMotionFXAllocator>::Destroy(); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::EnableRayRequests() { RaycastRequestBus::Handler::BusDisconnect(); RaycastRequestBus::Handler::BusConnect(); } void SystemComponent::DisableRayRequests() { RaycastRequestBus::Handler::BusDisconnect(); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams&) { #if !defined(AZ_MONOLITHIC_BUILD) // When module is linked dynamically, we must set our gEnv pointer. // When module is linked statically, we'll share the application's gEnv pointer. gEnv = system.GetGlobalEnvironment(); #endif REGISTER_CVAR2("emfx_updateEnabled", &emfx_updateEnabled, 1, VF_DEV_ONLY, "Enable main EMFX update"); REGISTER_CVAR2("emfx_actorRenderEnabled", &emfx_actorRenderEnabled, 1, VF_DEV_ONLY, "Enable ActorRenderNode rendering"); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::OnCryEditorInitialized() { // \todo Right now we're pointing at the @devassets@ location (source) and working from there, because .actor and .motion (motion) aren't yet processed through // the FBX pipeline. Once they are, we'll need to update various segments of the Tool to always read from the @assets@ cache, but write to the @devassets@ data/metadata. SetMediaRoot("@assets@"); EMotionFX::GetEMotionFX().InitAssetFolderPaths(); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::OnCrySystemShutdown(ISystem&) { gEnv->pConsole->UnregisterVariable("emfx_updateEnabled"); gEnv->pConsole->UnregisterVariable("emfx_actorRenderEnabled"); #if !defined(AZ_MONOLITHIC_BUILD) gEnv = nullptr; #endif } ////////////////////////////////////////////////////////////////////////// #if defined (EMOTIONFXANIMATION_EDITOR) void SystemComponent::UpdateAnimationEditorPlugins(float delta) { if (!EMStudio::GetManager()) { return; } EMStudio::PluginManager* pluginManager = EMStudio::GetPluginManager(); if (!pluginManager) { return; } // Process the plugins. const AZ::u32 numPlugins = pluginManager->GetNumActivePlugins(); for (AZ::u32 i = 0; i < numPlugins; ++i) { EMStudio::EMStudioPlugin* plugin = pluginManager->GetActivePlugin(i); plugin->ProcessFrame(delta); } } #endif ////////////////////////////////////////////////////////////////////////// void SystemComponent::OnTick(float delta, AZ::ScriptTimePoint timePoint) { AZ_UNUSED(timePoint); #if defined (EMOTIONFXANIMATION_EDITOR) AZ_UNUSED(delta); const float realDelta = m_updateTimer.StampAndGetDeltaTimeInSeconds(); // Flush events prior to updating EMotion FX. ActorNotificationBus::ExecuteQueuedEvents(); if (emfx_updateEnabled) { // Main EMotionFX runtime update. GetEMotionFX().Update(realDelta); } // Check if we are in game mode. IEditor* editor = nullptr; EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor); const bool inGameMode = editor ? editor->IsInGameMode() : false; // Update all the animation editor plugins (redraw viewports, timeline, and graph windows etc). // But only update this when the main window is visible and we are in game mode. const bool isEditorActive = EMotionFX::GetEMotionFX().GetIsInEditorMode() && EMStudio::GetManager() && EMStudio::HasMainWindow() && !EMStudio::GetMainWindow()->visibleRegion().isEmpty() && !inGameMode; if (isEditorActive) { UpdateAnimationEditorPlugins(realDelta); } #else // Flush events prior to updating EMotion FX. ActorNotificationBus::ExecuteQueuedEvents(); if (emfx_updateEnabled) { // Main EMotionFX runtime update. GetEMotionFX().Update(delta); } #endif const float timeDelta = delta; const ActorManager* actorManager = GetEMotionFX().GetActorManager(); const AZ::u32 numActorInstances = actorManager->GetNumActorInstances(); for (AZ::u32 i = 0; i < numActorInstances; ++i) { const ActorInstance* actorInstance = actorManager->GetActorInstance(i); if (actorInstance && actorInstance->GetIsEnabled() && actorInstance->GetIsOwnedByRuntime()) { AZ::Entity* entity = actorInstance->GetEntity(); const Actor* actor = actorInstance->GetActor(); if (entity && actor && actor->GetMotionExtractionNode()) { const AZ::EntityId entityId = entity->GetId(); // Check if we have any physics character controllers. bool hasCustomMotionExtractionController = false; bool hasPhysicsController = false; Physics::CharacterRequestBus::EventResult(hasPhysicsController, entityId, &Physics::CharacterRequests::IsPresent); if (!hasPhysicsController) { hasCustomMotionExtractionController = MotionExtractionRequestBus::FindFirstHandler(entityId) != nullptr; } // If we have a physics controller. if (hasCustomMotionExtractionController || hasPhysicsController) { const float deltaTimeInv = (timeDelta > 0.0f) ? (1.0f / timeDelta) : 0.0f; AZ::Transform currentTransform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); const AZ::Vector3 actorInstancePosition = actorInstance->GetWorldSpaceTransform().mPosition; const AZ::Vector3 positionDelta = actorInstancePosition - currentTransform.GetPosition(); if (hasPhysicsController) { Physics::CharacterRequestBus::Event(entityId, &Physics::CharacterRequests::TryRelativeMove, positionDelta, timeDelta); // Some of the character controller implementations like the PhysX one directly adjust the entity position and are not // delaying the calculation until the next physics system update. Thus, we will need to get the updated current transform. AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); } else if (hasCustomMotionExtractionController) { MotionExtractionRequestBus::Event(entityId, &MotionExtractionRequestBus::Events::ExtractMotion, positionDelta, timeDelta); AZ::TransformBus::EventResult(currentTransform, entityId, &AZ::TransformBus::Events::GetWorldTM); } // Calculate the difference in rotation and apply that to the entity transform. const AZ::Quaternion actorInstanceRotation = actorInstance->GetWorldSpaceTransform().mRotation; const AZ::Quaternion rotationDelta = AZ::Quaternion::CreateRotationFromScaledTransform(currentTransform).GetInverseFull() * actorInstanceRotation; if (!rotationDelta.IsIdentity(AZ::g_fltEps)) { currentTransform = currentTransform * AZ::Transform::CreateFromQuaternion(rotationDelta); AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, currentTransform); } } else // There is no physics controller, just use EMotion FX's actor instance transform directly. { const AZ::Transform newTransform = actorInstance->GetWorldSpaceTransform().ToAZTransform(); AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, newTransform); } } } } } int SystemComponent::GetTickOrder() { return AZ::TICK_ANIMATION; } ////////////////////////////////////////////////////////////////////////// void SystemComponent::RegisterAnimGraphObjectType(EMotionFX::AnimGraphObject* objectTemplate) { EMotionFX::AnimGraphObjectFactory::GetUITypes().emplace(azrtti_typeid(objectTemplate)); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::RegisterAssetTypesAndHandlers() { // Initialize asset handlers. m_assetHandlers.emplace_back(aznew ActorAssetHandler); m_assetHandlers.emplace_back(aznew MotionAssetHandler); m_assetHandlers.emplace_back(aznew MotionSetAssetHandler); m_assetHandlers.emplace_back(aznew AnimGraphAssetHandler); // Add asset types and extensions to AssetCatalog. auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); if (assetCatalog) { assetCatalog->EnableCatalogForAsset(azrtti_typeid<ActorAsset>()); assetCatalog->EnableCatalogForAsset(azrtti_typeid<MotionAsset>()); assetCatalog->EnableCatalogForAsset(azrtti_typeid<MotionSetAsset>()); assetCatalog->EnableCatalogForAsset(azrtti_typeid<AnimGraphAsset>()); assetCatalog->AddExtension("actor"); // Actor assetCatalog->AddExtension("motion"); // Motion assetCatalog->AddExtension("motionset"); // Motion set assetCatalog->AddExtension("animgraph"); // Anim graph } } ////////////////////////////////////////////////////////////////////////// void SystemComponent::SetMediaRoot(const char* alias) { const char* rootPath = AZ::IO::FileIOBase::GetInstance()->GetAlias(alias); if (rootPath) { AZStd::string mediaRootPath = rootPath; EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePathKeepCase, mediaRootPath); EMotionFX::GetEMotionFX().SetMediaRootFolder(mediaRootPath.c_str()); } else { AZ_Warning("EMotionFX", false, "Failed to set media root because alias \"%s\" could not be resolved.", alias); } } ////////////////////////////////////////////////////////////////////////// RaycastRequests::RaycastResult SystemComponent::Raycast(AZ::EntityId entityId, const RaycastRequests::RaycastRequest& rayRequest) { RaycastRequests::RaycastResult rayResult; // Build the ray request in the physics system. Physics::RayCastRequest physicsRayRequest; physicsRayRequest.m_start = rayRequest.m_start; physicsRayRequest.m_direction = rayRequest.m_direction; physicsRayRequest.m_distance = rayRequest.m_distance; physicsRayRequest.m_queryType = rayRequest.m_queryType; // Cast the ray in the physics system. Physics::RayCastHit physicsRayResult; Physics::WorldRequestBus::EventResult(physicsRayResult, Physics::DefaultPhysicsWorldId, &Physics::WorldRequests::RayCast, physicsRayRequest); if (physicsRayResult) // We intersected. { rayResult.m_position = physicsRayResult.m_position; rayResult.m_normal = physicsRayResult.m_normal; rayResult.m_intersected = true; } return rayResult; } #if defined (EMOTIONFXANIMATION_EDITOR) ////////////////////////////////////////////////////////////////////////// void InitializeEMStudioPlugins() { // Register EMFX plugins. EMStudio::PluginManager* pluginManager = EMStudio::GetPluginManager(); pluginManager->RegisterPlugin(new EMStudio::LogWindowPlugin()); pluginManager->RegisterPlugin(new EMStudio::CommandBarPlugin()); pluginManager->RegisterPlugin(new EMStudio::ActionHistoryPlugin()); pluginManager->RegisterPlugin(new EMStudio::MotionWindowPlugin()); pluginManager->RegisterPlugin(new EMStudio::MorphTargetsWindowPlugin()); pluginManager->RegisterPlugin(new EMStudio::TimeViewPlugin()); pluginManager->RegisterPlugin(new EMStudio::AttachmentsPlugin()); pluginManager->RegisterPlugin(new EMStudio::SceneManagerPlugin()); pluginManager->RegisterPlugin(new EMStudio::NodeWindowPlugin()); pluginManager->RegisterPlugin(new EMStudio::MotionEventsPlugin()); pluginManager->RegisterPlugin(new EMStudio::MotionSetsWindowPlugin()); pluginManager->RegisterPlugin(new EMStudio::NodeGroupsPlugin()); pluginManager->RegisterPlugin(new EMStudio::AnimGraphPlugin()); pluginManager->RegisterPlugin(new EMStudio::OpenGLRenderPlugin()); pluginManager->RegisterPlugin(new EMotionFX::HitDetectionJointInspectorPlugin()); pluginManager->RegisterPlugin(new EMotionFX::SkeletonOutlinerPlugin()); pluginManager->RegisterPlugin(new EMotionFX::RagdollNodeInspectorPlugin()); pluginManager->RegisterPlugin(new EMotionFX::ClothJointInspectorPlugin()); pluginManager->RegisterPlugin(new EMotionFX::SimulatedObjectWidget()); } ////////////////////////////////////////////////////////////////////////// void SystemComponent::NotifyRegisterViews() { using namespace AzToolsFramework; // Construct data folder that is used by the tool for loading assets (images etc.). AZStd::string devRootPath; AzFramework::ApplicationRequests::Bus::BroadcastResult(devRootPath, &AzFramework::ApplicationRequests::GetEngineRoot); devRootPath += "Gems/EMotionFX/Assets/Editor/"; AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, devRootPath); // Re-initialize EMStudio. int argc = 0; char** argv = nullptr; MysticQt::Initializer::Init("", devRootPath.c_str()); EMStudio::Initializer::Init(qApp, argc, argv); InitializeEMStudioPlugins(); // Get the MainWindow the first time so it is constructed EMStudio::GetManager()->GetMainWindow(); EMStudio::GetManager()->ExecuteApp(); AZStd::function<QWidget*(QWidget*)> windowCreationFunc = [](QWidget* parent = nullptr) { return EMStudio::GetMainWindow(); }; // Register EMotionFX window with the main editor. AzToolsFramework::ViewPaneOptions emotionFXWindowOptions; emotionFXWindowOptions.isPreview = true; emotionFXWindowOptions.isDeletable = true; emotionFXWindowOptions.isDockable = false; #if AZ_TRAIT_EMOTIONFX_MAIN_WINDOW_DETACHED emotionFXWindowOptions.detachedWindow = true; #endif emotionFXWindowOptions.optionalMenuText = "Animation Editor (PREVIEW)"; EditorRequests::Bus::Broadcast(&EditorRequests::RegisterViewPane, EMStudio::MainWindow::GetEMotionFXPaneName(), LyViewPane::CategoryTools, emotionFXWindowOptions, windowCreationFunc); } ////////////////////////////////////////////////////////////////////////// bool SystemComponent::IsSystemActive(EditorAnimationSystemRequests::AnimationSystem systemType) { return (systemType == AnimationSystem::EMotionFX); } // AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler AzToolsFramework::AssetBrowser::SourceFileDetails SystemComponent::GetSourceFileDetails(const char* fullSourceFileName) { using namespace AzToolsFramework::AssetBrowser; if (AZStd::wildcard_match("*.motionset", fullSourceFileName)) { return SourceFileDetails("Editor/Images/AssetBrowser/MotionSet_16.svg"); } else if (AZStd::wildcard_match("*.animgraph", fullSourceFileName)) { return SourceFileDetails("Editor/Images/AssetBrowser/AnimGraph_16.svg"); } return SourceFileDetails(); // no result } #endif // EMOTIONFXANIMATION_EDITOR } }