You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/EMotionFX/Code/EMotionFX/Source/ActorInstance.cpp

2092 lines
70 KiB
C++

/*
* 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 "EMotionFXConfig.h"
#include <MCore/Source/IDGenerator.h>
#include <MCore/Source/Random.h>
#include <MCore/Source/LogManager.h>
#include "Actor.h"
#include "ActorInstance.h"
#include "ActorManager.h"
#include "ActorUpdateScheduler.h"
#include "AnimGraphInstance.h"
#include "Attachment.h"
#include "AttachmentNode.h"
#include "AttachmentSkin.h"
#include "EventManager.h"
#include "Mesh.h"
#include "MeshDeformerStack.h"
#include "MorphMeshDeformer.h"
#include "MorphSetup.h"
#include "MorphSetupInstance.h"
#include "MotionLayerSystem.h"
#include "Node.h"
#include "NodeGroup.h"
#include "Recorder.h"
#include "TransformData.h"
#include <EMotionFX/Source/ActorInstanceBus.h>
#include <EMotionFX/Source/DebugDraw.h>
#include <EMotionFX/Source/RagdollInstance.h>
namespace EMotionFX
{
AZ_CLASS_ALLOCATOR_IMPL(ActorInstance, ActorInstanceAllocator, 0)
ActorInstance::ActorInstance(Actor* actor, AZ::Entity* entity, uint32 threadIndex)
: BaseObject()
, m_entity(entity)
, m_ragdollInstance(nullptr)
{
MCORE_ASSERT(actor);
// set the memory categories
mAttachments.SetMemoryCategory(EMFX_MEMCATEGORY_ACTORINSTANCES);
mDependencies.SetMemoryCategory(EMFX_MEMCATEGORY_ACTORINSTANCES);
mEnabledNodes.SetMemoryCategory(EMFX_MEMCATEGORY_ACTORINSTANCES);
mEnabledNodes.Reserve(actor->GetNumNodes());
// set the actor and create the motion system
mBoolFlags = 0;
mActor = actor;
mLODLevel = 0;
m_requestedLODLevel = 0;
mNumAttachmentRefs = 0;
mThreadIndex = threadIndex;
mAttachedTo = nullptr;
mSelfAttachment = nullptr;
mCustomData = nullptr;
mID = MCore::GetIDGenerator().GenerateID();
mVisualizeScale = 1.0f;
mMotionSamplingRate = 0.0f;
mMotionSamplingTimer = 0.0f;
mTrajectoryDelta.IdentityWithZeroScale();
mStaticAABB.Init();
mAnimGraphInstance = nullptr;
// set the boolean defaults
SetFlag(BOOL_ISVISIBLE, true);
SetFlag(BOOL_BOUNDSUPDATEENABLED, true);
SetFlag(BOOL_NORMALIZEDMOTIONLOD, true);
SetFlag(BOOL_RENDER, true);
SetFlag(BOOL_USEDFORVISUALIZATION, false);
SetFlag(BOOL_ENABLED, true);
SetFlag(BOOL_MOTIONEXTRACTION, true);
#if defined(EMFX_DEVELOPMENT_BUILD)
SetFlag(BOOL_OWNEDBYRUNTIME, false);
#endif // EMFX_DEVELOPMENT_BUILD
// enable all nodes on default
EnableAllNodes();
// apply actor node group default states (disable groups of nodes that are disabled on default)
const uint32 numGroups = mActor->GetNumNodeGroups();
for (uint32 i = 0; i < numGroups; ++i)
{
if (mActor->GetNodeGroup(i)->GetIsEnabledOnDefault() == false) // if this group is disabled on default
{
mActor->GetNodeGroup(i)->DisableNodes(this); // disable all nodes inside this group
}
}
// disable nodes that are disabled in LOD 0
Skeleton* skeleton = mActor->GetSkeleton();
const uint32 numNodes = skeleton->GetNumNodes();
for (uint32 n = 0; n < numNodes; ++n)
{
if (skeleton->GetNode(n)->GetSkeletalLODStatus(0) == false)
{
DisableNode(static_cast<uint16>(n));
}
}
// setup auto bounds update (it is enabled on default)
mBoundsUpdateFrequency = 0.0f;
mBoundsUpdatePassedTime = 0.0f;
mBoundsUpdateType = BOUNDS_STATIC_BASED;
mBoundsUpdateItemFreq = 1;
// initialize the actor local and global transform
mParentWorldTransform.Identity();
mLocalTransform.Identity();
mWorldTransform.Identity();
mWorldTransformInv.Identity();
// init the morph setup instance
mMorphSetup = MorphSetupInstance::Create();
mMorphSetup->Init(actor->GetMorphSetup(0));
// initialize the transformation data of this instance
mTransformData = TransformData::Create();
mTransformData->InitForActorInstance(this);
// create the motion system
mMotionSystem = MotionLayerSystem::Create(this);
// update the global and local matrices
UpdateTransformations(0.0f);
// update the actor dependencies
UpdateDependencies();
// update the static based AABB dimensions
mStaticAABB = mActor->GetStaticAABB();
if (mStaticAABB.CheckIfIsValid() == false)
{
UpdateMeshDeformers(0.0f, true); // TODO: not really thread safe because of shared meshes, although it probably will output correctly
UpdateStaticBasedAABBDimensions();
}
// update the bounds
UpdateBounds(0, mBoundsUpdateType, 1);
// register it
GetActorManager().RegisterActorInstance(this);
GetActorManager().GetScheduler()->RecursiveInsertActorInstance(this);
ActorInstanceNotificationBus::Broadcast(&ActorInstanceNotificationBus::Events::OnActorInstanceCreated, this);
}
ActorInstance::~ActorInstance()
{
ActorInstanceNotificationBus::Broadcast(&ActorInstanceNotificationBus::Events::OnActorInstanceDestroyed, this);
// get rid of the motion system
if (mMotionSystem)
{
mMotionSystem->Destroy();
}
if (mAnimGraphInstance)
{
mAnimGraphInstance->Destroy();
}
GetDebugDraw().UnregisterActorInstance(this);
// delete all attachments
// actor instances that are attached will be detached, and not deleted from memory
const uint32 numAttachments = mAttachments.GetLength();
for (uint32 i = 0; i < numAttachments; ++i)
{
ActorInstance* attachmentActorInstance = mAttachments[i]->GetAttachmentActorInstance();
if (attachmentActorInstance)
{
attachmentActorInstance->SetAttachedTo(nullptr);
attachmentActorInstance->SetSelfAttachment(nullptr);
attachmentActorInstance->DecreaseNumAttachmentRefs();
GetActorManager().UpdateActorInstanceStatus(attachmentActorInstance);
}
mAttachments[i]->Destroy();
}
mAttachments.Clear();
if (mMorphSetup)
{
mMorphSetup->Destroy();
}
if (mTransformData)
{
mTransformData->Destroy();
}
// remove the attachment from the actor instance where it is attached to
if (GetIsAttachment())
{
mAttachedTo->RemoveAttachment(this /*, false*/);
}
// automatically unregister the actor instance
GetActorManager().UnregisterActorInstance(this);
}
ActorInstance* ActorInstance::Create(Actor* actor, AZ::Entity* entity, uint32 threadIndex)
{
return aznew ActorInstance(actor, entity, threadIndex);
}
// update the transformation data
void ActorInstance::UpdateTransformations(float timePassedInSeconds, bool updateJointTransforms, bool sampleMotions)
{
// Update the LOD level in case a change was requested.
UpdateLODLevel();
const Recorder& recorder = GetRecorder();
timePassedInSeconds *= GetEMotionFX().GetGlobalSimulationSpeed();
// if we are using the recorder to playback
if (recorder.GetIsInPlayMode() && recorder.GetHasRecorded(this))
{
// output the anim graph instance, this doesn't overwrite transforms, just some things internally
if (recorder.GetRecordSettings().mRecordAnimGraphStates && mAnimGraphInstance)
{
mAnimGraphInstance->Update(0.0f);
mAnimGraphInstance->Output(nullptr);
}
// apply the main transformation
recorder.SampleAndApplyMainTransform(recorder.GetCurrentPlayTime(), this);
// apply the node transforms
if (recorder.GetRecordSettings().mRecordTransforms)
{
recorder.SampleAndApplyTransforms(recorder.GetCurrentPlayTime(), this);
}
// sample the morph targets
if (recorder.GetRecordSettings().mRecordMorphs)
{
recorder.SampleAndApplyMorphs(recorder.GetCurrentPlayTime(), this);
}
// perform forward kinematics etc
UpdateWorldTransform();
UpdateSkinningMatrices();
UpdateAttachments(); // update the attachment parent matrices
// update the bounds when needed
if (GetBoundsUpdateEnabled() && mBoundsUpdateType != BOUNDS_MESH_BASED)
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
{
UpdateBounds(mLODLevel, BOUNDS_NODE_BASED, mBoundsUpdateItemFreq);
mBoundsUpdatePassedTime = 0.0f;
}
}
return;
} // if the recorder is in playback mode and we recorded this actor instance
// check if we are an attachment
Attachment* attachment = GetSelfAttachment();
// if we are not an attachment, or if we are but not one influenced by multiple joints
if (!attachment || !attachment->GetIsInfluencedByMultipleJoints())
{
// update the motion system, which performs all blending, and updates all local transforms (excluding the local matrices)
if (mAnimGraphInstance)
{
mAnimGraphInstance->Update(timePassedInSeconds);
UpdateWorldTransform();
if (updateJointTransforms && sampleMotions)
{
mAnimGraphInstance->Output(mTransformData->GetCurrentPose());
if (m_ragdollInstance)
{
m_ragdollInstance->PostAnimGraphUpdate(timePassedInSeconds);
}
}
}
else if (mMotionSystem)
{
mMotionSystem->Update(timePassedInSeconds, (updateJointTransforms && sampleMotions));
}
else
{
UpdateWorldTransform();
}
// when the actor instance isn't visible, we don't want to do more things
if (!updateJointTransforms)
{
if (GetBoundsUpdateEnabled() && mBoundsUpdateType == BOUNDS_STATIC_BASED)
{
UpdateBounds(mLODLevel, mBoundsUpdateType);
}
return;
}
mTransformData->GetCurrentPose()->ApplyMorphWeightsToActorInstance();
ApplyMorphSetup();
UpdateSkinningMatrices();
UpdateAttachments();
}
else // we are a skin attachment
{
mLocalTransform.Identity();
if (mAnimGraphInstance)
{
mAnimGraphInstance->Update(timePassedInSeconds);
UpdateWorldTransform();
if (updateJointTransforms && sampleMotions)
{
mAnimGraphInstance->Output(mTransformData->GetCurrentPose());
}
}
else if (mMotionSystem)
{
mMotionSystem->Update(timePassedInSeconds, (updateJointTransforms && sampleMotions));
}
else
{
UpdateWorldTransform();
}
// when the actor instance isn't visible, we don't want to do more things
if (!updateJointTransforms)
{
if (GetBoundsUpdateEnabled() && mBoundsUpdateType == BOUNDS_STATIC_BASED)
{
UpdateBounds(mLODLevel, mBoundsUpdateType);
}
return;
}
mSelfAttachment->UpdateJointTransforms(*mTransformData->GetCurrentPose());
mTransformData->GetCurrentPose()->ApplyMorphWeightsToActorInstance();
ApplyMorphSetup();
UpdateSkinningMatrices();
UpdateAttachments();
}
// update the bounds when needed
if (GetBoundsUpdateEnabled() && mBoundsUpdateType != BOUNDS_MESH_BASED)
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
{
UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq);
mBoundsUpdatePassedTime = 0.0f;
}
}
}
// update the world transformation
void ActorInstance::UpdateWorldTransform()
{
mWorldTransform = mLocalTransform;
mWorldTransform.Multiply(mParentWorldTransform);
mWorldTransformInv = mWorldTransform.Inversed();
}
// updates the skinning matrices of all nodes
void ActorInstance::UpdateSkinningMatrices()
{
AZ::Matrix3x4* skinningMatrices = mTransformData->GetSkinningMatrices();
const Pose* pose = mTransformData->GetCurrentPose();
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
const uint32 nodeNumber = GetEnabledNode(i);
Transform skinningTransform = mActor->GetInverseBindPoseTransform(nodeNumber);
skinningTransform.Multiply(pose->GetModelSpaceTransform(nodeNumber));
skinningMatrices[nodeNumber] = AZ::Matrix3x4::CreateFromTransform(skinningTransform.ToAZTransform());
}
}
// Update the mesh deformers, which updates the vertex positions on the CPU, so performing CPU skinning and morphing etc.
void ActorInstance::UpdateMeshDeformers(float timePassedInSeconds, bool processDisabledDeformers)
{
timePassedInSeconds *= GetEMotionFX().GetGlobalSimulationSpeed();
// Update the mesh deformers.
const Skeleton* skeleton = mActor->GetSkeleton();
const uint32 numNodes = mEnabledNodes.GetLength();
for (uint32 i = 0; i < numNodes; ++i)
{
const uint16 nodeNr = mEnabledNodes[i];
Node* node = skeleton->GetNode(nodeNr);
MeshDeformerStack* stack = mActor->GetMeshDeformerStack(mLODLevel, nodeNr);
if (stack)
{
stack->Update(this, node, timePassedInSeconds, processDisabledDeformers);
}
}
// Update the bounds when we are set to use mesh based bounds.
if (GetBoundsUpdateEnabled() &&
GetBoundsUpdateType() == BOUNDS_MESH_BASED)
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
{
UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq);
mBoundsUpdatePassedTime = 0.0f;
}
}
}
// Update the mesh morph deformers, which updates the vertex positions on the CPU, so performing CPU morphing.
void ActorInstance::UpdateMorphMeshDeformers(float timePassedInSeconds, bool processDisabledDeformers)
{
timePassedInSeconds *= GetEMotionFX().GetGlobalSimulationSpeed();
// Update the mesh morph deformers.
const Skeleton* skeleton = mActor->GetSkeleton();
const uint32 numNodes = mEnabledNodes.GetLength();
for (uint32 i = 0; i < numNodes; ++i)
{
const uint16 nodeNr = mEnabledNodes[i];
Node* node = skeleton->GetNode(nodeNr);
MeshDeformerStack* stack = mActor->GetMeshDeformerStack(mLODLevel, nodeNr);
if (stack)
{
stack->UpdateByModifierType(this, node, timePassedInSeconds, MorphMeshDeformer::TYPE_ID, true, processDisabledDeformers);
}
}
// Update the bounds when we are set to use mesh based bounds.
if (GetBoundsUpdateEnabled() &&
GetBoundsUpdateType() == BOUNDS_MESH_BASED)
{
mBoundsUpdatePassedTime += timePassedInSeconds;
if (mBoundsUpdatePassedTime >= mBoundsUpdateFrequency)
{
UpdateBounds(mLODLevel, mBoundsUpdateType, mBoundsUpdateItemFreq);
mBoundsUpdatePassedTime = 0.0f;
}
}
}
void ActorInstance::PostPhysicsUpdate(float timePassedInSeconds)
{
if (m_ragdollInstance)
{
m_ragdollInstance->PostPhysicsUpdate(timePassedInSeconds);
}
}
// add an attachment
void ActorInstance::AddAttachment(Attachment* attachment)
{
AZ_Assert(attachment, "Attachment cannot be a nullptr");
AZ_Assert(attachment->GetAttachmentActorInstance() != this, "Cannot attach to itself.");
// first remove the current attachment tree from the scheduler
ActorInstance* root = FindAttachmentRoot();
GetActorManager().GetScheduler()->RecursiveRemoveActorInstance(root);
// add the attachment
mAttachments.Add(attachment);
ActorInstance* attachmentActorInstance = attachment->GetAttachmentActorInstance();
if (attachmentActorInstance)
{
attachmentActorInstance->IncreaseNumAttachmentRefs();
attachmentActorInstance->SetAttachedTo(this);
GetActorManager().UpdateActorInstanceStatus(attachmentActorInstance);
}
// and re-add the root to the scheduler
//GetActorManager().GetScheduler()->RecursiveInsertActorInstance(root, 0);
// re-add the root if it was visible already
//if (root->GetIsVisible())
GetActorManager().GetScheduler()->RecursiveInsertActorInstance(root);
}
// try to find the attachment number for a given actor instance
uint32 ActorInstance::FindAttachmentNr(ActorInstance* actorInstance)
{
// for all attachments
const uint32 numAttachments = mAttachments.GetLength();
for (uint32 i = 0; i < numAttachments; ++i)
{
if (mAttachments[i]->GetAttachmentActorInstance() == actorInstance)
{
return i;
}
}
return MCORE_INVALIDINDEX32;
}
// remove an attachment by actor instance pointer
bool ActorInstance::RemoveAttachment(ActorInstance* actorInstance, bool delFromMem)
{
// try to find the attachment
const uint32 attachmentNr = FindAttachmentNr(actorInstance);
if (attachmentNr == MCORE_INVALIDINDEX32)
{
return false;
}
// remove the actual attachment
RemoveAttachment(attachmentNr, delFromMem);
return true;
}
// remove an attachment
void ActorInstance::RemoveAttachment(uint32 nr, bool delFromMem)
{
MCORE_ASSERT(nr < mAttachments.GetLength());
// first remove the current attachment tree from the scheduler
ActorInstance* root = FindAttachmentRoot();
GetActorManager().GetScheduler()->RecursiveRemoveActorInstance(root);
// get the attachment
Attachment* attachment = mAttachments[nr];
// its not an attachment anymore
ActorInstance* attachmentInstance = attachment->GetAttachmentActorInstance();
if (attachmentInstance)
{
attachmentInstance->SetSelfAttachment(nullptr);
// unregister the attachment inside the actor instance that represents the attachment
attachmentInstance->DecreaseNumAttachmentRefs();
attachmentInstance->SetAttachedTo(nullptr);
GetActorManager().UpdateActorInstanceStatus(attachmentInstance);
attachmentInstance->SetParentWorldSpaceTransform(Transform::CreateIdentity());
}
// delete it from memory when desired
if (delFromMem)
{
attachment->Destroy();
}
// remove it from the attachment list
mAttachments.Remove(nr);
// and re-add the root to the scheduler
GetActorManager().GetScheduler()->RecursiveInsertActorInstance(root, 0);
// re-add the attachment
if (attachmentInstance)
{
GetActorManager().GetScheduler()->RecursiveInsertActorInstance(attachmentInstance, 0);
}
}
// remove all attachments
void ActorInstance::RemoveAllAttachments(bool delFromMem)
{
// keep removing the last attachment until there are none left
while (mAttachments.GetLength())
{
RemoveAttachment(mAttachments.GetLength() - 1, delFromMem);
}
}
// update the dependencies
void ActorInstance::UpdateDependencies()
{
// get rid of existing dependencies
mDependencies.Clear();
// add the main dependency
Actor::Dependency mainDependency;
mainDependency.mActor = mActor;
mainDependency.mAnimGraph = (mAnimGraphInstance) ? mAnimGraphInstance->GetAnimGraph() : nullptr;
mDependencies.Add(mainDependency);
// add all dependencies stored inside the actor
const uint32 numDependencies = mActor->GetNumDependencies();
for (uint32 i = 0; i < numDependencies; ++i)
{
mDependencies.Add(*mActor->GetDependency(i));
}
}
// set the attachment matrices
void ActorInstance::UpdateAttachments()
{
// update all attachments
const uint32 numAttachments = mAttachments.GetLength();
for (uint32 i = 0; i < numAttachments; ++i)
{
mAttachments[i]->Update();
}
}
// find the root attachment actor instance, for example if you have a
// knight with knife attached to his hands, where the knight is attached to a horse, the horse will be the
// attachment root
ActorInstance* ActorInstance::FindAttachmentRoot() const
{
if (mAttachedTo)
{
return mAttachedTo->FindAttachmentRoot();
}
return const_cast<ActorInstance*>(this);
}
// change visibility state
void ActorInstance::SetIsVisible(bool isVisible)
{
// if the state doesn't change, do nothing
if (isVisible == GetIsVisible())
{
return;
}
// if we change visibility state
SetFlag(BOOL_ISVISIBLE, isVisible);
}
// update the bounding volume
void ActorInstance::UpdateBounds(uint32 geomLODLevel, EBoundsType boundsType, uint32 itemFrequency)
{
// depending on the bounding volume update type
switch (boundsType)
{
// calculate the static based AABB
case BOUNDS_STATIC_BASED:
CalcStaticBasedAABB(&mAABB);
break;
// based on the world space positions of the nodes (least accurate, but fastest)
case BOUNDS_NODE_BASED:
CalcNodeBasedAABB(&mAABB, itemFrequency);
break;
// based on the world space positions of the vertices of the collision meshes (faster and more accurate than mesh based)
case BOUNDS_COLLISIONMESH_BASED:
CalcCollisionMeshBasedAABB(geomLODLevel, &mAABB, itemFrequency);
break;
// based on the world space positions of the vertices of the meshes (most accurate)
case BOUNDS_MESH_BASED:
CalcMeshBasedAABB(geomLODLevel, &mAABB, itemFrequency);
break;
// based on the world space positions of the vertices of the meshes (most accurate)
case BOUNDS_NODEOBB_BASED:
CalcNodeOBBBasedAABB(&mAABB, itemFrequency);
break;
case BOUNDS_NODEOBBFAST_BASED:
CalcNodeOBBBasedAABBFast(&mAABB, itemFrequency);
break;
// when we're dealing with an unspecified bounding volume update method
default:
MCore::LogInfo("*** EMotionFX::ActorInstance::UpdateBounds() - Unknown boundsType specified! (%d) ***", (uint32)boundsType);
}
}
// calculate the axis aligned bounding box that contains the object oriented boxes of all nodes
void ActorInstance::CalcNodeOBBBasedAABBFast(MCore::AABB* outResult, uint32 nodeFrequency)
{
// init the axis aligned bounding box
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; i += nodeFrequency)
{
nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
if (node->GetIncludeInBoundsCalc())
{
const MCore::OBB& obb = mActor->GetNodeOBB(nodeNr);
if (obb.CheckIfIsValid() == false)
{
continue;
}
// calculate the corner points of the node in local space
AZ::Vector3 minPoint, maxPoint;
obb.CalcMinMaxPoints(&minPoint, &maxPoint);
// encapsulate the results in the AABB box
const Transform worldTransform = pose->GetWorldSpaceTransform(nodeNr);
outResult->Encapsulate(worldTransform.TransformPoint(minPoint));
outResult->Encapsulate(worldTransform.TransformPoint(maxPoint));
}
}
}
// more accurate node obb based method that uses the 8 corner points of the obb
void ActorInstance::CalcNodeOBBBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency)
{
// init the axis aligned bounding box
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
AZ::Vector3 cornerPoints[8];
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; i += nodeFrequency)
{
nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
if (node->GetIncludeInBoundsCalc())
{
const MCore::OBB& obb = mActor->GetNodeOBB(nodeNr);
if (obb.CheckIfIsValid() == false)
{
continue;
}
// calculate the 8 corner points
obb.CalcCornerPoints(cornerPoints);
const Transform worldTransform = pose->GetWorldSpaceTransform(nodeNr);
// encapsulate all OBB world space corner points inside the AABB
for (uint32 p = 0; p < 8; ++p)
{
outResult->Encapsulate(worldTransform.TransformPoint(cornerPoints[p]));
}
}
}
}
// calculate the axis aligned bounding box based on the world space positions of the nodes
void ActorInstance::CalcNodeBasedAABB(MCore::AABB* outResult, uint32 nodeFrequency)
{
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; i += nodeFrequency)
{
nodeNr = GetEnabledNode(i);
if (skeleton->GetNode(nodeNr)->GetIncludeInBoundsCalc())
{
outResult->Encapsulate(pose->GetWorldSpaceTransform(nodeNr).mPosition);
}
}
}
// calculate the AABB that contains all world space vertices of all meshes
void ActorInstance::CalcMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency)
{
// init the axis aligned bounding box
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
const uint16 nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
// skip nodes without meshes
Mesh* mesh = mActor->GetMesh(geomLODLevel, nodeNr);
if (mesh == nullptr)
{
continue;
}
// if this node should be excluded
if (node->GetIncludeInBoundsCalc() == false)
{
continue;
}
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(geomLODLevel, nodeNr);
// calculate and encapsulate the mesh bounds inside the total mesh box
MCore::AABB meshBox;
mesh->CalcAABB(&meshBox, worldTransform, vertexFrequency);
outResult->Encapsulate(meshBox);
}
}
void ActorInstance::CalcCollisionMeshBasedAABB(uint32 geomLODLevel, MCore::AABB* outResult, uint32 vertexFrequency)
{
// init the axis aligned bounding box
outResult->Init();
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes, encapsulate the world space positions
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
// skip nodes without collision meshes
Mesh* mesh = mActor->GetMesh(geomLODLevel, nodeNr);
if (mesh == nullptr)
{
continue;
}
if (mesh->GetIsCollisionMesh() == false)
{
continue;
}
// if this node should be excluded
if (node->GetIncludeInBoundsCalc() == false)
{
continue;
}
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(geomLODLevel, nodeNr);
// calculate and encapsulate the mesh bounds inside the total mesh box
MCore::AABB meshBox;
mesh->CalcAABB(&meshBox, worldTransform, vertexFrequency);
outResult->Encapsulate(meshBox);
}
}
// setup bounding volume auto update settings
void ActorInstance::SetupAutoBoundsUpdate(float updateFrequencyInSeconds, EBoundsType boundsType, uint32 itemFrequency)
{
MCORE_ASSERT(itemFrequency > 0); // zero would cause an infinite loop
mBoundsUpdateFrequency = updateFrequencyInSeconds;
mBoundsUpdateType = boundsType;
mBoundsUpdateItemFreq = itemFrequency;
SetBoundsUpdateEnabled(true);
}
// apply the morph setup
void ActorInstance::ApplyMorphSetup()
{
// get the morph setup instance and original setup
MorphSetupInstance* morphSetupInstance = GetMorphSetupInstance();
if (morphSetupInstance == nullptr)
{
return;
}
// if there is no morph setup, we have nothing to do
MorphSetup* morphSetup = mActor->GetMorphSetup(mLODLevel);
if (morphSetup == nullptr)
{
return;
}
// apply all morph targets
//bool allZero = true;
const uint32 numTargets = morphSetup->GetNumMorphTargets();
for (uint32 i = 0; i < numTargets; ++i)
{
// get the morph target
MorphTarget* morphTarget = morphSetup->GetMorphTarget(i);
MorphSetupInstance::MorphTarget* morphTargetInstance = morphSetupInstance->FindMorphTargetByID(morphTarget->GetID());
if (morphTargetInstance == nullptr)
{
continue;
}
// get the weight for this morph target
const float weight = morphTargetInstance->GetWeight();
// apply the morph target if its weight is non-zero
if (MCore::Math::Abs(weight) > 0.0001f)
{
//allZero = false;
morphTarget->Apply(this, weight);
}
}
/*
// enable or disable all morph deformers if the weights are all zero
const uint32 numNodes = mActor->GetNumNodes();
for (uint32 n=0; n<numNodes; ++n)
{
Node* node = mActor->GetNode(n);
MeshDeformerStack* stack = node->GetMeshDeformerStack( mGeometryLODLevel ).GetPointer();
if (stack == nullptr)
continue;
stack->EnableAllDeformersByType( MorphMeshDeformer::TYPE_ID, !allZero );
}*/
}
//---------------------
// check intersection with a ray, but don't get the intersection point or closest intersecting node
Node* ActorInstance::IntersectsCollisionMesh(uint32 lodLevel, const MCore::Ray& ray) const
{
const Skeleton* skeleton = mActor->GetSkeleton();
const Pose* pose = mTransformData->GetCurrentPose();
// for all nodes
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
const uint16 nodeNr = GetEnabledNode(i);
// check if there is a mesh for this node
Mesh* mesh = mActor->GetMesh(lodLevel, nodeNr);
if (mesh == nullptr)
{
continue;
}
if (mesh->GetIsCollisionMesh() == false)
{
continue;
}
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(lodLevel, nodeNr);
// return when there is an intersection
if (mesh->Intersects(worldTransform, ray))
{
return skeleton->GetNode(nodeNr);
}
}
return nullptr;
}
Node* ActorInstance::IntersectsCollisionMesh(uint32 lodLevel, const MCore::Ray& ray, AZ::Vector3* outIntersect, AZ::Vector3* outNormal, AZ::Vector2* outUV, float* outBaryU, float* outBaryV, uint32* outIndices) const
{
Node* closestNode = nullptr;
AZ::Vector3 point;
AZ::Vector3 closestPoint(0.0f, 0.0f, 0.0f);
float dist, baryU, baryV, closestBaryU = 0, closestBaryV = 0;
float closestDist = FLT_MAX;
Transform closestTransform = Transform::CreateIdentity();
uint32 closestIndices[3];
uint32 triIndices[3];
const Skeleton* skeleton = mActor->GetSkeleton();
const Pose* pose = mTransformData->GetCurrentPose();
// check all nodes
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; i++)
{
nodeNr = GetEnabledNode(i);
Node* curNode = skeleton->GetNode(nodeNr);
Mesh* mesh = mActor->GetMesh(lodLevel, nodeNr);
if (mesh == nullptr)
{
continue;
}
if (mesh->GetIsCollisionMesh() == false)
{
continue;
}
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(lodLevel, nodeNr);
// if there is an intersection between the ray and the mesh
if (mesh->Intersects(worldTransform, ray, &point, &baryU, &baryV, triIndices))
{
// calculate the squared distance between the intersection point and the ray origin
dist = (point - ray.GetOrigin()).GetLengthSq();
// if it is the closest point till now, record it as closest
if (dist < closestDist)
{
closestTransform = worldTransform;
closestPoint = point;
closestDist = dist;
closestNode = curNode;
closestBaryU = baryU;
closestBaryV = baryV;
closestIndices[0] = triIndices[0];
closestIndices[1] = triIndices[1];
closestIndices[2] = triIndices[2];
}
}
}
// output the closest values
if (closestNode)
{
if (outIntersect)
{
*outIntersect = closestPoint;
}
if (outBaryU)
{
*outBaryU = closestBaryU;
}
if (outBaryV)
{
*outBaryV = closestBaryV;
}
if (outIndices)
{
outIndices[0] = closestIndices[0];
outIndices[1] = closestIndices[1];
outIndices[2] = closestIndices[2];
}
// calculate the interpolated normal
if (outNormal || outUV)
{
Mesh* mesh = mActor->GetMesh(lodLevel, closestNode->GetNodeIndex());
// calculate the normal at the intersection point
if (outNormal)
{
AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(Mesh::ATTRIB_NORMALS);
AZ::Vector3 norm = MCore::BarycentricInterpolate<AZ::Vector3>(closestBaryU, closestBaryV, normals[closestIndices[0]], normals[closestIndices[1]], normals[closestIndices[2]]);
norm = closestTransform.TransformVector(norm);
norm.Normalize();
*outNormal = norm;
}
// calculate the UV coordinate at the intersection point
if (outUV)
{
// get the UV coordinates of the first layer
AZ::Vector2* uvData = static_cast<AZ::Vector2*>(mesh->FindVertexData(Mesh::ATTRIB_UVCOORDS, 0));
if (uvData)
{
// calculate the interpolated texture coordinate
*outUV = MCore::BarycentricInterpolate<AZ::Vector2>(closestBaryU, closestBaryV, uvData[closestIndices[0]], uvData[closestIndices[1]], uvData[closestIndices[2]]);
}
}
}
}
// return the result
return closestNode;
}
// check intersection with a ray, but don't get the intersection point or closest intersecting node
Node* ActorInstance::IntersectsMesh(uint32 lodLevel, const MCore::Ray& ray) const
{
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// for all nodes
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
nodeNr = GetEnabledNode(i);
Node* node = skeleton->GetNode(nodeNr);
// check if there is a mesh for this node
Mesh* mesh = mActor->GetMesh(lodLevel, nodeNr);
if (mesh == nullptr)
{
continue;
}
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(lodLevel, nodeNr);
// return when there is an intersection
if (mesh->Intersects(worldTransform, ray))
{
return node;
}
}
// no intersection found
return nullptr;
}
void ActorInstance::SetRagdoll(Physics::Ragdoll* ragdoll)
{
if (ragdoll && ragdoll->GetNumNodes() > 0)
{
m_ragdollInstance = AZStd::make_unique<RagdollInstance>(ragdoll, this);
}
else
{
m_ragdollInstance.reset();
}
}
RagdollInstance* ActorInstance::GetRagdollInstance() const
{
return m_ragdollInstance.get();
}
// intersection test that returns the closest intersection
Node* ActorInstance::IntersectsMesh(uint32 lodLevel, const MCore::Ray& ray, AZ::Vector3* outIntersect, AZ::Vector3* outNormal, AZ::Vector2* outUV, float* outBaryU, float* outBaryV, uint32* outIndices) const
{
Node* closestNode = nullptr;
AZ::Vector3 point;
AZ::Vector3 closestPoint(0.0f, 0.0f, 0.0f);
Transform closestTransform = Transform::CreateIdentity();
float dist, baryU, baryV, closestBaryU = 0, closestBaryV = 0;
float closestDist = FLT_MAX;
uint32 closestIndices[3];
uint32 triIndices[3];
const Pose* pose = mTransformData->GetCurrentPose();
const Skeleton* skeleton = mActor->GetSkeleton();
// check all nodes
uint16 nodeNr;
const uint32 numNodes = GetNumEnabledNodes();
for (uint32 i = 0; i < numNodes; i++)
{
nodeNr = GetEnabledNode(i);
Node* curNode = skeleton->GetNode(nodeNr);
Mesh* mesh = mActor->GetMesh(lodLevel, nodeNr);
if (mesh == nullptr)
{
continue;
}
const Transform worldTransform = pose->GetMeshNodeWorldSpaceTransform(lodLevel, nodeNr);
// if there is an intersection between the ray and the mesh
if (mesh->Intersects(worldTransform, ray, &point, &baryU, &baryV, triIndices))
{
// calculate the squared distance between the intersection point and the ray origin
dist = (point - ray.GetOrigin()).GetLengthSq();
// if it is the closest point till now, record it as closest
if (dist < closestDist)
{
closestTransform = worldTransform;
closestPoint = point;
closestDist = dist;
closestNode = curNode;
closestBaryU = baryU;
closestBaryV = baryV;
closestIndices[0] = triIndices[0];
closestIndices[1] = triIndices[1];
closestIndices[2] = triIndices[2];
}
}
}
// output the closest values
if (closestNode)
{
if (outIntersect)
{
*outIntersect = closestPoint;
}
if (outBaryU)
{
*outBaryU = closestBaryU;
}
if (outBaryV)
{
*outBaryV = closestBaryV;
}
if (outIndices)
{
outIndices[0] = closestIndices[0];
outIndices[1] = closestIndices[1];
outIndices[2] = closestIndices[2];
}
// calculate the interpolated normal
if (outNormal || outUV)
{
Mesh* mesh = mActor->GetMesh(lodLevel, closestNode->GetNodeIndex());
// calculate the normal at the intersection point
if (outNormal)
{
AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(Mesh::ATTRIB_NORMALS);
AZ::Vector3 norm = MCore::BarycentricInterpolate<AZ::Vector3>(
closestBaryU, closestBaryV,
normals[closestIndices[0]], normals[closestIndices[1]], normals[closestIndices[2]]);
norm = closestTransform.TransformVector(norm);
norm.Normalize();
*outNormal = norm;
}
// calculate the UV coordinate at the intersection point
if (outUV)
{
// get the UV coordinates of the first layer
AZ::Vector2* uvData = static_cast<AZ::Vector2*>(mesh->FindVertexData(Mesh::ATTRIB_UVCOORDS, 0));
if (uvData)
{
// calculate the interpolated texture coordinate
*outUV = MCore::BarycentricInterpolate<AZ::Vector2>(closestBaryU, closestBaryV, uvData[closestIndices[0]], uvData[closestIndices[1]], uvData[closestIndices[2]]);
}
}
}
}
// return the result
return closestNode;
}
//---------------------
//
void ActorInstance::EnableNode(uint16 nodeIndex)
{
// if this node already is at an enabled state, do nothing
if (mEnabledNodes.Contains(nodeIndex))
{
return;
}
Skeleton* skeleton = mActor->GetSkeleton();
// find the location where to insert (as the flattened hierarchy needs to be preserved in the array)
bool found = false;
uint32 curNode = nodeIndex;
do
{
// get the parent of the current node
uint32 parentIndex = skeleton->GetNode(curNode)->GetParentIndex();
if (parentIndex != MCORE_INVALIDINDEX32)
{
const uint32 parentArrayIndex = mEnabledNodes.Find(static_cast<uint16>(parentIndex));
if (parentArrayIndex != MCORE_INVALIDINDEX32)
{
if (parentArrayIndex + 1 >= mEnabledNodes.GetLength())
{
mEnabledNodes.Add(nodeIndex);
}
else
{
mEnabledNodes.Insert(parentArrayIndex + 1, nodeIndex);
}
found = true;
}
else
{
curNode = parentIndex;
}
}
else // if we're dealing with a root node, insert it in the front of the array
{
mEnabledNodes.Insert(0, nodeIndex);
found = true;
}
} while (found == false);
}
// disable a given node
void ActorInstance::DisableNode(uint16 nodeIndex)
{
// try to remove the node from the array
mEnabledNodes.RemoveByValue(nodeIndex);
}
// enable all nodes
void ActorInstance::EnableAllNodes()
{
const uint32 numNodes = mActor->GetNumNodes();
mEnabledNodes.Resize(numNodes);
for (uint32 i = 0; i < numNodes; ++i)
{
mEnabledNodes[i] = static_cast<uint16>(i);
}
}
// disable all nodes
void ActorInstance::DisableAllNodes()
{
mEnabledNodes.Clear();
}
// change the skeletal LOD level
void ActorInstance::SetSkeletalLODLevelNodeFlags(uint32 level)
{
// make sure the lod level is in range of 0..31
const uint32 newLevel = MCore::Clamp<uint32>(level, 0, 31);
// if the lod level is the same as it currently is, do nothing
if (newLevel == mLODLevel)
{
return;
}
Skeleton* skeleton = mActor->GetSkeleton();
// change the state of all nodes that need state changes
const uint32 numNodes = GetNumNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
Node* node = skeleton->GetNode(i);
// check the curent and the new enabled state
const bool curEnabled = node->GetSkeletalLODStatus(mLODLevel);
const bool newEnabled = node->GetSkeletalLODStatus(newLevel);
// if the state changed, enable or disable it
if (curEnabled != newEnabled)
{
if (newEnabled) // if the new LOD says that this node should be enabled, enable it
{
EnableNode(static_cast<uint16>(i));
}
else // otherwise disable it
{
DisableNode(static_cast<uint16>(i));
}
}
}
}
void ActorInstance::SetLODLevel(uint32 level)
{
m_requestedLODLevel = level;
}
void ActorInstance::UpdateLODLevel()
{
// Switch LOD level in case a change was requested.
if (mLODLevel != m_requestedLODLevel)
{
// Enable and disable all nodes accordingly (do not call this after setting the new mLODLevel)
SetSkeletalLODLevelNodeFlags(m_requestedLODLevel);
// Make sure the LOD level is valid and update it.
mLODLevel = MCore::Clamp<uint32>(m_requestedLODLevel, 0, mActor->GetNumLODLevels() - 1);
/*// update the transform data
MorphSetup* morphSetup = mActor->GetMorphSetup(mLODLevel);
if (morphSetup)
mTransformData->SetNumMorphWeights( morphSetup->GetNumMorphTargets() );
else
mTransformData->SetNumMorphWeights( 0 );*/
}
}
// enable or disable nodes based on the skeletal LOD flags
void ActorInstance::UpdateSkeletalLODFlags()
{
// change the state of all nodes that need state changes
Skeleton* skeleton = mActor->GetSkeleton();
const uint32 numNodes = skeleton->GetNumNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
Node* node = skeleton->GetNode(i);
// if the new LOD says that this node should be enabled, enable it
if (node->GetSkeletalLODStatus(mLODLevel))
{
EnableNode(static_cast<uint16>(i));
}
else // otherwise disable it
{
DisableNode(static_cast<uint16>(i));
}
}
}
// calculate the number of disabled nodes for a given skeletal lod level
uint32 ActorInstance::CalcNumDisabledNodes(uint32 skeletalLODLevel) const
{
uint32 numDisabledNodes = 0;
Skeleton* skeleton = mActor->GetSkeleton();
// get the number of nodes and iterate through them
const uint32 numNodes = GetNumNodes();
for (uint32 i = 0; i < numNodes; ++i)
{
// get the current node
Node* node = skeleton->GetNode(i);
// check if the current node is disabled in the given skeletal lod level, if yes increase the resulting number
if (node->GetSkeletalLODStatus(skeletalLODLevel) == false)
{
numDisabledNodes++;
}
}
return numDisabledNodes;
}
// calculate the number of skeletal LOD levels
uint32 ActorInstance::CalcNumSkeletalLODLevels() const
{
uint32 numSkeletalLODLevels = 0;
// iterate over all skeletal LOD levels
uint32 currentNumDisabledNodes = 0;
uint32 previousNumDisabledNodes = MCORE_INVALIDINDEX32;
for (uint32 i = 0; i < 32; ++i)
{
// get the number of disabled nodes in the current skeletal LOD level
currentNumDisabledNodes = CalcNumDisabledNodes(i);
// compare the number of disabled nodes from the current skeletal LOD level with the previous one and increase the number of
// skeletal LOD levels in case they are not equal, if they are equal we have reached the last skeletal LOD level and we can skip the further calculations
if (previousNumDisabledNodes != currentNumDisabledNodes)
{
numSkeletalLODLevels++;
previousNumDisabledNodes = currentNumDisabledNodes;
}
else
{
break;
}
}
return numSkeletalLODLevels;
}
// change the current motion system
void ActorInstance::SetMotionSystem(MotionSystem* newSystem, bool delCurrentFromMem)
{
if (delCurrentFromMem && mMotionSystem)
{
mMotionSystem->Destroy();
}
mMotionSystem = newSystem;
}
// check if this actor instance is a skin attachment
bool ActorInstance::GetIsSkinAttachment() const
{
if (mSelfAttachment == nullptr)
{
return false;
}
return mSelfAttachment->GetIsInfluencedByMultipleJoints();
}
// draw a skeleton using lines, calling the drawline callbacks in the event handlers
void ActorInstance::DrawSkeleton(const Pose& pose, const AZ::Color& color)
{
DebugDraw& debugDraw = GetDebugDraw();
DebugDraw::ActorInstanceData* drawData = debugDraw.GetActorInstanceData(this);
drawData->Lock();
drawData->DrawPose(pose, color);
drawData->Unlock();
}
// Remove the trajectory transform from the input transformation.
void ActorInstance::MotionExtractionCompensate(Transform& inOutMotionExtractionNodeTransform, const Transform& localSpaceBindPoseTransform, EMotionExtractionFlags motionExtractionFlags)
{
Transform trajectoryTransform = inOutMotionExtractionNodeTransform;
// Make sure the z axis is really pointing up and project it onto the ground plane.
const AZ::Vector3 forwardAxis = MCore::CalcForwardAxis(trajectoryTransform.mRotation);
if (forwardAxis.GetZ() > 0.0f) // Pick the closest, so if we point more upwards already, we take 1.0, otherwise take -1.0. Sometimes Y would point up, sometimes down.
{
MCore::RotateFromTo(trajectoryTransform.mRotation, forwardAxis, AZ::Vector3(0.0f, 0.0f, 1.0f));
}
else
{
MCore::RotateFromTo(trajectoryTransform.mRotation, forwardAxis, AZ::Vector3(0.0f, 0.0f, -1.0f));
}
trajectoryTransform.ApplyMotionExtractionFlags(motionExtractionFlags);
// Get the projected bind pose transform.
Transform bindTransformProjected = localSpaceBindPoseTransform;
bindTransformProjected.ApplyMotionExtractionFlags(motionExtractionFlags);
// Remove the projected rotation and translation from the transform to prevent the double transform.
inOutMotionExtractionNodeTransform.mRotation = (bindTransformProjected.mRotation.GetConjugate() * trajectoryTransform.mRotation).GetConjugate() * inOutMotionExtractionNodeTransform.mRotation;
inOutMotionExtractionNodeTransform.mPosition = inOutMotionExtractionNodeTransform.mPosition - (trajectoryTransform.mPosition - bindTransformProjected.mPosition);
inOutMotionExtractionNodeTransform.mRotation.Normalize();
}
void ActorInstance::MotionExtractionCompensate(Transform& inOutMotionExtractionNodeTransform, EMotionExtractionFlags motionExtractionFlags) const
{
MCORE_ASSERT(mActor->GetMotionExtractionNodeIndex() != MCORE_INVALIDINDEX32);
Transform bindPoseTransform = mTransformData->GetBindPose()->GetLocalSpaceTransform(mActor->GetMotionExtractionNodeIndex());
MotionExtractionCompensate(inOutMotionExtractionNodeTransform, bindPoseTransform, motionExtractionFlags);
}
// Remove the trajectory transform from the motion extraction node to prevent double transformation.
void ActorInstance::MotionExtractionCompensate(EMotionExtractionFlags motionExtractionFlags)
{
const uint32 motionExtractIndex = mActor->GetMotionExtractionNodeIndex();
if (motionExtractIndex == MCORE_INVALIDINDEX32)
{
return;
}
Pose* currentPose = mTransformData->GetCurrentPose();
Transform transform = currentPose->GetLocalSpaceTransform(motionExtractIndex);
MotionExtractionCompensate(transform, motionExtractionFlags);
currentPose->SetLocalSpaceTransform(motionExtractIndex, transform);
}
void ActorInstance::ApplyMotionExtractionDelta(Transform& inOutTransform, const Transform& trajectoryDelta)
{
Transform curTransform = inOutTransform;
#ifndef EMFX_SCALE_DISABLED
curTransform.mPosition += trajectoryDelta.mPosition * curTransform.mScale;
#else
curTransform.mPosition += trajectoryDelta.mPosition;
#endif
curTransform.mRotation *= trajectoryDelta.mRotation;
curTransform.mRotation.Normalize();
inOutTransform = curTransform;
}
// Apply the motion extraction delta transform to the actor instance.
void ActorInstance::ApplyMotionExtractionDelta(const Transform& trajectoryDelta)
{
if (mActor->GetMotionExtractionNodeIndex() == MCORE_INVALIDINDEX32)
{
return;
}
ApplyMotionExtractionDelta(mLocalTransform, trajectoryDelta);
}
// apply the currently set motion extraction delta transform to the actor instance
void ActorInstance::ApplyMotionExtractionDelta()
{
ApplyMotionExtractionDelta(mTrajectoryDelta);
}
void ActorInstance::SetMotionExtractionEnabled(bool enabled)
{
SetFlag(BOOL_MOTIONEXTRACTION, enabled);
}
bool ActorInstance::GetMotionExtractionEnabled() const
{
return (mBoolFlags & BOOL_MOTIONEXTRACTION) != 0;
}
// update the static based aabb dimensions
void ActorInstance::UpdateStaticBasedAABBDimensions()
{
// backup the transform
Transform orgTransform = GetLocalSpaceTransform();
//-------------------------------------
// reset position and scale
SetLocalSpacePosition(AZ::Vector3::CreateZero());
EMFX_SCALECODE(
SetLocalSpaceScale(AZ::Vector3(1.0f, 1.0f, 1.0f));)
// rotate over x, y and z axis
AZ::Vector3 boxMin(FLT_MAX, FLT_MAX, FLT_MAX);
AZ::Vector3 boxMax(-FLT_MAX, -FLT_MAX, -FLT_MAX);
for (uint32 axis = 0; axis < 3; axis++)
{
for (uint32 i = 0; i < 360; i += 45) // steps of 45 degrees
{
// rotate a given amount of degrees over the axis we are currently testing
AZ::Vector3 axisVector(0.0f, 0.0f, 0.0f);
axisVector.SetElement(axis, 1.0f);
const float angle = static_cast<float>(i);
SetLocalSpaceRotation(MCore::CreateFromAxisAndAngle(axisVector, MCore::Math::DegreesToRadians(angle)));
UpdateTransformations(0.0f, true);
UpdateMeshDeformers(0.0f);
// calculate the aabb of this
if (mActor->CheckIfHasMeshes(0))
{
CalcMeshBasedAABB(0, &mStaticAABB);
}
else
{
CalcNodeBasedAABB(&mStaticAABB);
}
// find the minimum and maximum
const AZ::Vector3& curMin = mStaticAABB.GetMin();
const AZ::Vector3& curMax = mStaticAABB.GetMax();
if (curMin.GetX() < boxMin.GetX())
{
boxMin.SetX(curMin.GetX());
}
if (curMin.GetY() < boxMin.GetY())
{
boxMin.SetY(curMin.GetY());
}
if (curMin.GetZ() < boxMin.GetZ())
{
boxMin.SetZ(curMin.GetZ());
}
if (curMax.GetX() > boxMax.GetX())
{
boxMax.SetX(curMax.GetX());
}
if (curMax.GetY() > boxMax.GetY())
{
boxMax.SetY(curMax.GetY());
}
if (curMax.GetZ() > boxMax.GetZ())
{
boxMax.SetZ(curMax.GetZ());
}
}
}
mStaticAABB.SetMin(boxMin);
mStaticAABB.SetMax(boxMax);
/*
// calculate the center point of the box
const AZ::Vector3 center = mStaticAABB.CalcMiddle();
// find the maximum of the width, height and depth
const float maxDim = MCore::Max3<float>( mStaticAABB.CalcWidth(), mStaticAABB.CalcHeight(), mStaticAABB.CalcDepth() ) * 0.5f;
// make width, height and depth the same as its maximum
mStaticAABB.SetMin( center + AZ::Vector3(-maxDim, -maxDim, -maxDim) );
mStaticAABB.SetMax( center + AZ::Vector3( maxDim, maxDim, maxDim) );
*/
//-------------------------------------
// restore the transform
mLocalTransform = orgTransform;
}
// calculate the moved static based aabb
void ActorInstance::CalcStaticBasedAABB(MCore::AABB* outResult)
{
if (GetIsSkinAttachment())
{
mSelfAttachment->GetAttachToActorInstance()->CalcStaticBasedAABB(outResult);
return;
}
*outResult = mStaticAABB;
EMFX_SCALECODE(
outResult->SetMin(mStaticAABB.GetMin() * mWorldTransform.mScale);
outResult->SetMax(mStaticAABB.GetMax() * mWorldTransform.mScale);)
outResult->Translate(mWorldTransform.mPosition);
}
// adjust the animgraph instance
void ActorInstance::SetAnimGraphInstance(AnimGraphInstance* instance)
{
mAnimGraphInstance = instance;
UpdateDependencies();
}
Actor* ActorInstance::GetActor() const
{
return mActor;
}
void ActorInstance::SetID(uint32 id)
{
mID = id;
}
MotionSystem* ActorInstance::GetMotionSystem() const
{
return mMotionSystem;
}
uint32 ActorInstance::GetLODLevel() const
{
return mLODLevel;
}
void ActorInstance::SetCustomData(void* customData)
{
mCustomData = customData;
}
void* ActorInstance::GetCustomData() const
{
return mCustomData;
}
AZ::Entity* ActorInstance::GetEntity() const
{
return m_entity;
}
AZ::EntityId ActorInstance::GetEntityId() const
{
if (m_entity)
{
return m_entity->GetId();
}
return AZ::EntityId();
}
bool ActorInstance::GetBoundsUpdateEnabled() const
{
return (mBoolFlags & BOOL_BOUNDSUPDATEENABLED);
}
float ActorInstance::GetBoundsUpdateFrequency() const
{
return mBoundsUpdateFrequency;
}
float ActorInstance::GetBoundsUpdatePassedTime() const
{
return mBoundsUpdatePassedTime;
}
ActorInstance::EBoundsType ActorInstance::GetBoundsUpdateType() const
{
return mBoundsUpdateType;
}
uint32 ActorInstance::GetBoundsUpdateItemFrequency() const
{
return mBoundsUpdateItemFreq;
}
void ActorInstance::SetBoundsUpdateFrequency(float seconds)
{
mBoundsUpdateFrequency = seconds;
}
void ActorInstance::SetBoundsUpdatePassedTime(float seconds)
{
mBoundsUpdatePassedTime = seconds;
}
void ActorInstance::SetBoundsUpdateType(EBoundsType bType)
{
mBoundsUpdateType = bType;
}
void ActorInstance::SetBoundsUpdateItemFrequency(uint32 freq)
{
MCORE_ASSERT(freq >= 1);
mBoundsUpdateItemFreq = freq;
}
void ActorInstance::SetBoundsUpdateEnabled(bool enable)
{
SetFlag(BOOL_BOUNDSUPDATEENABLED, enable);
}
void ActorInstance::SetStaticBasedAABB(const MCore::AABB& aabb)
{
mStaticAABB = aabb;
}
void ActorInstance::GetStaticBasedAABB(MCore::AABB* outAABB)
{
*outAABB = mStaticAABB;
}
const MCore::AABB& ActorInstance::GetStaticBasedAABB() const
{
return mStaticAABB;
}
const MCore::AABB& ActorInstance::GetAABB() const
{
return mAABB;
}
void ActorInstance::SetAABB(const MCore::AABB& aabb)
{
mAABB = aabb;
}
uint32 ActorInstance::GetNumAttachments() const
{
return mAttachments.GetLength();
}
Attachment* ActorInstance::GetAttachment(uint32 nr) const
{
return mAttachments[nr];
}
bool ActorInstance::GetIsAttachment() const
{
return (mAttachedTo != nullptr);
}
ActorInstance* ActorInstance::GetAttachedTo() const
{
return mAttachedTo;
}
Attachment* ActorInstance::GetSelfAttachment() const
{
return mSelfAttachment;
}
uint32 ActorInstance::GetNumDependencies() const
{
return mDependencies.GetLength();
}
Actor::Dependency* ActorInstance::GetDependency(uint32 nr)
{
return &mDependencies[nr];
}
MorphSetupInstance* ActorInstance::GetMorphSetupInstance() const
{
return mMorphSetup;
}
void ActorInstance::SetParentWorldSpaceTransform(const Transform& transform)
{
mParentWorldTransform = transform;
}
const Transform& ActorInstance::GetParentWorldSpaceTransform() const
{
return mParentWorldTransform;
}
void ActorInstance::SetRender(bool enabled)
{
SetFlag(BOOL_RENDER, enabled);
}
bool ActorInstance::GetRender() const
{
return (mBoolFlags & BOOL_RENDER) != 0;
}
void ActorInstance::SetIsUsedForVisualization(bool enabled)
{
SetFlag(BOOL_USEDFORVISUALIZATION, enabled);
}
bool ActorInstance::GetIsUsedForVisualization() const
{
return (mBoolFlags & BOOL_USEDFORVISUALIZATION) != 0;
}
void ActorInstance::SetIsOwnedByRuntime(bool isOwnedByRuntime)
{
#if defined(EMFX_DEVELOPMENT_BUILD)
SetFlag(BOOL_OWNEDBYRUNTIME, isOwnedByRuntime);
#else
AZ_UNUSED(isOwnedByRuntime);
#endif
}
bool ActorInstance::GetIsOwnedByRuntime() const
{
#if defined(EMFX_DEVELOPMENT_BUILD)
return (mBoolFlags & BOOL_OWNEDBYRUNTIME) != 0;
#else
return true;
#endif
}
uint32 ActorInstance::GetThreadIndex() const
{
return mThreadIndex;
}
void ActorInstance::SetThreadIndex(uint32 index)
{
mThreadIndex = index;
}
void ActorInstance::SetTrajectoryDeltaTransform(const Transform& transform)
{
mTrajectoryDelta = transform;
}
const Transform& ActorInstance::GetTrajectoryDeltaTransform() const
{
return mTrajectoryDelta;
}
AnimGraphPose* ActorInstance::RequestPose(uint32 threadIndex)
{
return GetEMotionFX().GetThreadData(threadIndex)->GetPosePool().RequestPose(this);
}
void ActorInstance::FreePose(uint32 threadIndex, AnimGraphPose* pose)
{
GetEMotionFX().GetThreadData(threadIndex)->GetPosePool().FreePose(pose);
}
void ActorInstance::SetMotionSamplingTimer(float timeInSeconds)
{
mMotionSamplingTimer = timeInSeconds;
}
void ActorInstance::SetMotionSamplingRate(float updateRateInSeconds)
{
mMotionSamplingRate = updateRateInSeconds;
}
float ActorInstance::GetMotionSamplingTimer() const
{
return mMotionSamplingTimer;
}
float ActorInstance::GetMotionSamplingRate() const
{
return mMotionSamplingRate;
}
void ActorInstance::IncreaseNumAttachmentRefs(uint8 numToIncreaseWith)
{
mNumAttachmentRefs += numToIncreaseWith;
MCORE_ASSERT(mNumAttachmentRefs == 0 || mNumAttachmentRefs == 1);
}
void ActorInstance::DecreaseNumAttachmentRefs(uint8 numToDecreaseWith)
{
mNumAttachmentRefs -= numToDecreaseWith;
MCORE_ASSERT(mNumAttachmentRefs == 0 || mNumAttachmentRefs == 1);
}
uint8 ActorInstance::GetNumAttachmentRefs() const
{
return mNumAttachmentRefs;
}
void ActorInstance::SetAttachedTo(ActorInstance* actorInstance)
{
mAttachedTo = actorInstance;
}
void ActorInstance::SetSelfAttachment(Attachment* selfAttachment)
{
mSelfAttachment = selfAttachment;
}
void ActorInstance::EnableFlag(uint8 flag)
{
mBoolFlags |= flag;
}
void ActorInstance::DisableFlag(uint8 flag)
{
mBoolFlags &= ~flag;
}
void ActorInstance::SetFlag(uint8 flag, bool enabled)
{
if (enabled)
{
mBoolFlags |= flag;
}
else
{
mBoolFlags &= ~flag;
}
}
void ActorInstance::RecursiveSetIsVisible(bool isVisible)
{
SetIsVisible(isVisible);
// recurse to all child attachments
const uint32 numAttachments = mAttachments.GetLength();
for (uint32 i = 0; i < numAttachments; ++i)
{
mAttachments[i]->GetAttachmentActorInstance()->RecursiveSetIsVisible(isVisible);
}
}
void ActorInstance::RecursiveSetIsVisibleTowardsRoot(bool isVisible)
{
SetIsVisible(isVisible);
if (mSelfAttachment)
{
mSelfAttachment->GetAttachToActorInstance()->RecursiveSetIsVisibleTowardsRoot(isVisible);
}
}
void ActorInstance::SetIsEnabled(bool enabled)
{
SetFlag(BOOL_ENABLED, enabled);
}
// update the normal scale factor based on the bounds
void ActorInstance::UpdateVisualizeScale()
{
mVisualizeScale = 0.0f;
UpdateMeshDeformers(0.0f);
MCore::AABB box;
CalcCollisionMeshBasedAABB(0, &box);
if (box.CheckIfIsValid())
{
mVisualizeScale = MCore::Max<float>(mVisualizeScale, box.CalcRadius());
}
CalcNodeBasedAABB(&box);
if (box.CheckIfIsValid())
{
mVisualizeScale = MCore::Max<float>(mVisualizeScale, box.CalcRadius());
}
CalcMeshBasedAABB(0, &box);
if (box.CheckIfIsValid())
{
mVisualizeScale = MCore::Max<float>(mVisualizeScale, box.CalcRadius());
}
mVisualizeScale *= 0.01f;
}
// get the normal scale factor
float ActorInstance::GetVisualizeScale() const
{
return mVisualizeScale;
}
// manually set the visualize scale factor
void ActorInstance::SetVisualizeScale(float factor)
{
mVisualizeScale = factor;
}
// Recursively check if we have a given attachment in the hierarchy going downwards.
bool ActorInstance::RecursiveHasAttachment(const ActorInstance* attachmentInstance) const
{
if (attachmentInstance == this)
{
return true;
}
// Iterate down the chain of attachments.
const AZ::u32 numAttachments = GetNumAttachments();
for (AZ::u32 i = 0; i < numAttachments; ++i)
{
if (GetAttachment(i)->GetAttachmentActorInstance()->RecursiveHasAttachment(attachmentInstance))
{
return true;
}
}
return false;
}
// Check if we can safely attach an attachment that uses the specified actor instance.
// This will check for infinite recursion/circular chains.
bool ActorInstance::CheckIfCanHandleAttachment(const ActorInstance* attachmentInstance) const
{
if (RecursiveHasAttachment(attachmentInstance) || attachmentInstance->RecursiveHasAttachment(this))
{
return false;
}
return true;
}
} // namespace EMotionFX