Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "GameProject_precompiled.h"
- ////#pragma optimize("", off)
- #include <AzCore/Serialization/EditContext.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <EMotionFX/Source/ActorInstance.h>
- #include <EMotionFX/Source/AnimGraph.h>
- #include <EMotionFX/Source/AnimGraphManager.h>
- #include <EMotionFX/Source/Parameter/Parameter.h>
- #include <EMotionFX/Source/Parameter/FloatParameter.h>
- #include <EMotionFX/Source/Parameter/DefaultValueParameter.h>
- #include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
- #include <EMotionFX/Source/ConstraintTransformRotationAngles.h>
- #include <EMotionFX/Source/DebugDraw.h>
- #include <EMotionFX/Source/EMotionFXConfig.h>
- #include <EMotionFX/Source/EventManager.h>
- #include <EMotionFX/Source/Node.h>
- #include <EMotionFX/Source/TransformData.h>
- #include <MCore/Source/AttributeVector2.h>
- #include <MCore/Source/AzCoreConversions.h>
- #include <MCore/Source/Compare.h>
- #include "MyEFXNode.h"
- namespace EMotionFX
- {
- AZ_CLASS_ALLOCATOR_IMPL(MyEFXNode, AnimGraphAllocator, 0)
- AZ_CLASS_ALLOCATOR_IMPL(MyEFXNode::UniqueData, AnimGraphObjectUniqueDataAllocator, 0)
- MyEFXNode::MyEFXNode()
- : AnimGraphNode()
- , m_constraintRotation(AZ::Quaternion::CreateIdentity())
- , m_postRotation(AZ::Quaternion::CreateIdentity())
- , m_limitMin(-90.0f, -50.0f)
- , m_limitMax(90.0f, 30.0f)
- , m_followSpeed(0.75f)
- , m_twistAxis(ConstraintTransformRotationAngles::AXIS_Y)
- , m_limitsEnabled(false)
- , m_smoothing(true)
- , m_worldPos(AZ::Vector3::CreateZero())
- {
- // setup the input ports
- InitInputPorts(4);
- SetupInputPort("Pose", INPUTPORT_POSE, AttributePose::TYPE_ID, PORTID_INPUT_POSE);
- SetupInputPortAsVector3("Goal Pos", INPUTPORT_GOALPOS, PORTID_INPUT_GOALPOS);
- SetupInputPortAsNumber("Weight", INPUTPORT_WEIGHT, PORTID_INPUT_WEIGHT);
- SetupInputPortAsVector3("DebugVec3", INPUTPORT_DEBUGVEC, PORTID_INPUT_DEBUGVEC);
- // setup the output ports
- InitOutputPorts(1);
- SetupOutputPortAsPose("Output Pose", OUTPUTPORT_POSE, PORTID_OUTPUT_POSE);
- }
- MyEFXNode::~MyEFXNode()
- {
- }
- void MyEFXNode::Reinit()
- {
- // add include #include <EMotionFX/Source/Parameter/FloatSliderParameter.h>
- // add var def to class header EMotionFX::FloatSliderParameter fParam;
- // everytime then this node reinits it add param to theirs anim graph
- // you can add a bunch of params here.
- // and make a special dummy efx node what only fill graph with some sets of params
- fParam.SetName("auto_parameter");
- fParam.SetDescription("description");
- fParam.SetDefaultValue(3.14f);
- fParam.SetMaxValue(10.f);
- fParam.SetMinValue(0.f);
- mAnimGraph->AddParameter(&fParam);
- const size_t numAnimGraphInstances = mAnimGraph->GetNumAnimGraphInstances();
- for (size_t i = 0; i < numAnimGraphInstances; ++i)
- {
- AnimGraphInstance* animGraphInstance = mAnimGraph->GetAnimGraphInstance(i);
- UniqueData* uniqueData = static_cast<UniqueData*>(FindUniqueNodeData(animGraphInstance));
- if (uniqueData)
- {
- uniqueData->mMustUpdate = true;
- }
- OnUpdateUniqueData(animGraphInstance);
- }
- AnimGraphNode::Reinit();
- }
- bool MyEFXNode::InitAfterLoading(AnimGraph* animGraph)
- {
- if (!AnimGraphNode::InitAfterLoading(animGraph))
- {
- return false;
- }
- InitInternalAttributesForAllInstances();
- Reinit();
- return true;
- }
- // get the palette name
- const char* MyEFXNode::GetPaletteName() const
- {
- return "MyEFXNode";
- }
- // get the category
- AnimGraphObject::ECategory MyEFXNode::GetPaletteCategory() const
- {
- return AnimGraphObject::CATEGORY_CONTROLLERS;
- }
- // pre-create unique data object
- void MyEFXNode::OnUpdateUniqueData(AnimGraphInstance* animGraphInstance)
- {
- // find the unique data for this node, if it doesn't exist yet, create it
- UniqueData* uniqueData = static_cast<UniqueData*>(animGraphInstance->FindUniqueObjectData(this));
- if (uniqueData == nullptr)
- {
- uniqueData = aznew UniqueData(this, animGraphInstance);
- animGraphInstance->RegisterUniqueObjectData(uniqueData);
- }
- uniqueData->mMustUpdate = true;
- UpdateUniqueData(animGraphInstance, uniqueData);
- }
- // the main process method of the final node
- void MyEFXNode::Output(AnimGraphInstance* animGraphInstance)
- {
- AnimGraphPose* outputPose;
- //bool AnimGraph::AddParameter(Parameter* parameter, const GroupParameter* parentGroup)
- // make sure we have at least an input pose, otherwise output the bind pose
- if (GetInputPort(INPUTPORT_POSE).mConnection == nullptr)
- {
- RequestPoses(animGraphInstance);
- outputPose = GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue();
- ActorInstance* actorInstance = animGraphInstance->GetActorInstance();
- outputPose->InitFromBindPose(actorInstance);
- return;
- }
- // get the weight
- float weight = 1.0f;
- if (GetInputPort(INPUTPORT_WEIGHT).mConnection)
- {
- OutputIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_WEIGHT));
- weight = GetInputNumberAsFloat(animGraphInstance, INPUTPORT_WEIGHT);
- weight = MCore::Clamp<float>(weight, 0.0f, 1.0f);
- }
- // if the weight is near zero, we can skip all calculations and act like a pass-trough node
- if (weight < MCore::Math::epsilon || mDisabled)
- {
- OutputIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_POSE));
- RequestPoses(animGraphInstance);
- outputPose = GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue();
- const AnimGraphPose* inputPose = GetInputPose(animGraphInstance, INPUTPORT_POSE)->GetValue();
- *outputPose = *inputPose;
- UniqueData* uniqueData = static_cast<UniqueData*>(FindUniqueNodeData(animGraphInstance));
- UpdateUniqueData(animGraphInstance, uniqueData); // update the unique data (lookup node indices when something changed)
- uniqueData->mFirstUpdate = true;
- return;
- }
- // get the input pose and copy it over to the output pose
- OutputIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_POSE));
- RequestPoses(animGraphInstance);
- outputPose = GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue();
- const AnimGraphPose* inputPose = GetInputPose(animGraphInstance, INPUTPORT_POSE)->GetValue();
- *outputPose = *inputPose;
- //------------------------------------
- // get the node indices to work on
- //------------------------------------
- UniqueData* uniqueData = static_cast<UniqueData*>(FindUniqueNodeData(animGraphInstance));
- UpdateUniqueData(animGraphInstance, uniqueData); // update the unique data (lookup node indices when something changed)
- if (uniqueData->mIsValid == false)
- {
- if (GetEMotionFX().GetIsInEditorMode())
- {
- uniqueData->mMustUpdate = true;
- UpdateUniqueData(animGraphInstance, uniqueData);
- if (uniqueData->mIsValid == false)
- {
- SetHasError(animGraphInstance, true);
- }
- }
- return;
- }
- // there is no error
- if (GetEMotionFX().GetIsInEditorMode())
- {
- SetHasError(animGraphInstance, false);
- }
- // get the goal
- OutputIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_GOALPOS));
- AZ::Vector3 goal = AZ::Vector3::CreateZero();
- TryGetInputVector3(animGraphInstance, INPUTPORT_GOALPOS, goal);
- // get the debug vec
- OutputIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_DEBUGVEC));
- AZ::Vector3 debugvec = AZ::Vector3::CreateZero();
- TryGetInputVector3(animGraphInstance, INPUTPORT_DEBUGVEC, debugvec);
- ActorInstance* actorInstance = animGraphInstance->GetActorInstance();
- // get a shortcut to the local transform object
- const uint32 nodeIndex = uniqueData->mNodeIndex;
- // get start and end
- const uint32 startNodeIndex = uniqueData->mStartNodeIndex;
- const uint32 endNodeIndex = uniqueData->mEndNodeIndex;
- Pose& outTransformPose = outputPose->GetPose();
- Transform globalTransform = outTransformPose.GetWorldSpaceTransform(nodeIndex);
- // get global start and end
- Transform startGlobalTransform = outTransformPose.GetWorldSpaceTransform(startNodeIndex);
- Transform endGlobalTransform = outTransformPose.GetWorldSpaceTransform(endNodeIndex);
- globalTransform = startGlobalTransform.Blend(endGlobalTransform, weight);
- outTransformPose.SetWorldSpaceTransform(nodeIndex, globalTransform);
- Skeleton* skeleton = actorInstance->GetActor()->GetSkeleton();
- //// Prevent invalid float values inside the LookAt matrix construction when both position and goal are the same
- //const AZ::Vector3 diff = globalTransform.mPosition - goal;
- //if (diff.GetLengthSq() < AZ::g_fltEps)
- //{
- // goal += AZ::Vector3(0.0f, 0.000001f, 0.0f);
- //}
- //// calculate the lookat transform
- //// TODO: a quaternion lookat function would be nicer, so that there are no matrix operations involved
- //MCore::Matrix lookAt;
- //lookAt.LookAt(globalTransform.mPosition, goal, AZ::Vector3(0.0f, 0.0f, 1.0f));
- //MCore::Quaternion destRotation = lookAt.Transposed(); // implicit matrix to quat conversion
- // // apply the post rotation
- //MCore::Quaternion postRotation = MCore::AzQuatToEmfxQuat(m_postRotation);
- //destRotation = destRotation * postRotation;
- //// get the constraint rotation
- //const MCore::Quaternion constraintRotation = MCore::AzQuatToEmfxQuat(m_constraintRotation);
- //if (m_limitsEnabled)
- //{
- // // calculate the delta between the bind pose rotation and current target rotation and constraint that to our limits
- // const uint32 parentIndex = skeleton->GetNode(nodeIndex)->GetParentIndex();
- // MCore::Quaternion parentRotationGlobal;
- // MCore::Quaternion bindRotationLocal;
- // if (parentIndex != MCORE_INVALIDINDEX32)
- // {
- // parentRotationGlobal = inputPose->GetPose().GetWorldSpaceTransform(parentIndex).mRotation;
- // bindRotationLocal = actorInstance->GetTransformData()->GetBindPose()->GetLocalSpaceTransform(parentIndex).mRotation;
- // }
- // else
- // {
- // parentRotationGlobal.Identity();
- // bindRotationLocal.Identity();
- // }
- // const MCore::Quaternion destRotationLocal = destRotation * parentRotationGlobal.Conjugated();
- // const MCore::Quaternion deltaRotLocal = destRotationLocal * bindRotationLocal.Conjugated();
- // // setup the constraint and execute it
- // // the limits are relative to the bind pose in local space
- // ConstraintTransformRotationAngles constraint; // TODO: place this inside the unique data? would be faster, but takes more memory, or modify the constraint to support internal modification of a transform directly
- // constraint.SetMinRotationAngles(m_limitMin);
- // constraint.SetMaxRotationAngles(m_limitMax);
- // constraint.SetMinTwistAngle(0.0f);
- // constraint.SetMaxTwistAngle(0.0f);
- // constraint.SetTwistAxis(m_twistAxis);
- // constraint.GetTransform().mRotation = (deltaRotLocal * constraintRotation.Conjugated());
- // constraint.Execute();
- // if (GetEMotionFX().GetIsInEditorMode() && GetCanVisualize(animGraphInstance))
- // {
- // MCore::Matrix offset = (postRotation.Inversed() * bindRotationLocal * constraintRotation * parentRotationGlobal).ToMatrix();
- // offset.SetTranslation(globalTransform.mPosition);
- // constraint.DebugDraw(actorInstance, offset, GetVisualizeColor(), 0.5f);
- // }
- // // convert back into world space
- // destRotation = (bindRotationLocal * (constraint.GetTransform().mRotation * constraintRotation)) * parentRotationGlobal;
- //}
- //// init the rotation quaternion to the initial rotation
- //if (uniqueData->mFirstUpdate)
- //{
- // uniqueData->mRotationQuat = destRotation;
- // uniqueData->mFirstUpdate = false;
- //}
- //// interpolate between the current rotation and the destination rotation
- //if (m_smoothing)
- //{
- // const float speed = m_followSpeed * uniqueData->mTimeDelta * 10.0f;
- // if (speed < 1.0f)
- // {
- // uniqueData->mRotationQuat = uniqueData->mRotationQuat.Slerp(destRotation, speed);
- // }
- // else
- // {
- // uniqueData->mRotationQuat = destRotation;
- // }
- //}
- //else
- //{
- // uniqueData->mRotationQuat = destRotation;
- //}
- //uniqueData->mRotationQuat.Normalize();
- //globalTransform.mRotation = uniqueData->mRotationQuat;
- //// only blend when needed
- //if (weight < 0.999f)
- //{
- // outTransformPose.SetWorldSpaceTransform(nodeIndex, globalTransform);
- // const Pose& inputTransformPose = inputPose->GetPose();
- // Transform finalTransform = inputTransformPose.GetLocalSpac eTransform(nodeIndex);
- // finalTransform.Blend(outTransformPose.GetLocalSpaceTransform(nodeIndex), weight);
- // outTransformPose.SetLocalSpaceTransform(nodeIndex, finalTransform);
- //}
- //else
- //{
- // outTransformPose.SetWorldSpaceTransform(nodeIndex, globalTransform);
- //}
- // perform some debug rendering
- //if (GetEMotionFX().GetIsInEditorMode() && GetCanVisualize(animGraphInstance))
- if (GetEMotionFX().GetIsInEditorMode() && GetCanVisualize(animGraphInstance))
- {
- const float s = animGraphInstance->GetVisualizeScale() * actorInstance->GetVisualizeScale();
- DebugDraw& debugDraw = GetDebugDraw();
- DebugDraw::ActorInstanceData* drawData = debugDraw.GetActorInstanceData(animGraphInstance->GetActorInstance());
- drawData->Lock();
- drawData->Clear();
- drawData->DrawLine(goal - AZ::Vector3(s, 0, 0), goal + AZ::Vector3(s, 0, 0), mVisualizeColor);
- drawData->DrawLine(goal - AZ::Vector3(0, s, 0), goal + AZ::Vector3(0, s, 0), mVisualizeColor);
- drawData->DrawLine(goal - AZ::Vector3(0, 0, s), goal + AZ::Vector3(0, 0, s), mVisualizeColor);
- drawData->DrawLine(globalTransform.mPosition, goal, mVisualizeColor);
- drawData->DrawLine(globalTransform.mPosition, globalTransform.mPosition + globalTransform.mRotation.CalcUpAxis() * s * 50.0f, AZ::Color(0.0f, 0.0f, 1.0f, 1.0f));
- drawData->DrawWireframeSphere(debugvec, s * 1.0f, AZ::Color(1.0f, 1.0f, 1.0f, 1.0f), AZ::Quaternion::CreateIdentity());
- drawData->Unlock();
- }
- }
- // update the unique data
- void MyEFXNode::UpdateUniqueData(AnimGraphInstance* animGraphInstance, UniqueData* uniqueData)
- {
- // update the unique data if needed
- if (uniqueData->mMustUpdate)
- {
- ActorInstance* actorInstance = animGraphInstance->GetActorInstance();
- Actor* actor = actorInstance->GetActor();
- // don't update the next time again
- uniqueData->mMustUpdate = false;
- // get the node
- uniqueData->mNodeIndex = MCORE_INVALIDINDEX32;
- uniqueData->mIsValid = false;
- if (m_targetNodeName.empty())
- {
- return;
- }
- if (m_startNodeName.empty())
- {
- return;
- }
- if (m_endNodeName.empty())
- {
- return;
- }
- const Node* targetNode = actor->GetSkeleton()->FindNodeByName(m_targetNodeName.c_str());
- if (!targetNode)
- {
- return;
- }
- const Node* startNode = actor->GetSkeleton()->FindNodeByName(m_startNodeName.c_str());
- if (!startNode)
- {
- return;
- }
- const Node* endNode = actor->GetSkeleton()->FindNodeByName(m_endNodeName.c_str());
- if (!endNode)
- {
- return;
- }
- uniqueData->mNodeIndex = targetNode->GetNodeIndex();
- uniqueData->mStartNodeIndex = startNode->GetNodeIndex();
- uniqueData->mEndNodeIndex = endNode->GetNodeIndex();
- uniqueData->mIsValid = true;
- }
- }
- // update
- void MyEFXNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds)
- {
- // update the weight node
- AnimGraphNode* weightNode = GetInputNode(INPUTPORT_WEIGHT);
- if (weightNode)
- {
- UpdateIncomingNode(animGraphInstance, weightNode, timePassedInSeconds);
- }
- // update the goal node
- AnimGraphNode* goalNode = GetInputNode(INPUTPORT_GOALPOS);
- if (goalNode)
- {
- UpdateIncomingNode(animGraphInstance, goalNode, timePassedInSeconds);
- }
- // update the debugvec
- AnimGraphNode* debugvecNode = GetInputNode(INPUTPORT_DEBUGVEC);
- if (debugvecNode)
- {
- UpdateIncomingNode(animGraphInstance, debugvecNode, timePassedInSeconds);
- }
- // update the pose node
- UniqueData* uniqueData = static_cast<UniqueData*>(animGraphInstance->FindUniqueObjectData(this));
- AnimGraphNode* inputNode = GetInputNode(INPUTPORT_POSE);
- if (inputNode)
- {
- UpdateIncomingNode(animGraphInstance, inputNode, timePassedInSeconds);
- // update the sync track
- uniqueData->Init(animGraphInstance, inputNode);
- }
- else
- {
- uniqueData->Clear();
- }
- uniqueData->mTimeDelta = timePassedInSeconds;
- }
- AZ::Crc32 MyEFXNode::GetLimitWidgetsVisibility() const
- {
- return m_limitsEnabled ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
- }
- AZ::Crc32 MyEFXNode::GetFollowSpeedVisibility() const
- {
- return m_smoothing ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
- }
- void MyEFXNode::SetTargetNodeName(const AZStd::string& targetNodeName)
- {
- m_targetNodeName = targetNodeName;
- }
- void MyEFXNode::SetStartNodeName(const AZStd::string& startNodeName)
- {
- m_startNodeName = startNodeName;
- }
- void MyEFXNode::SetEndNodeName(const AZStd::string& endNodeName)
- {
- m_endNodeName = endNodeName;
- }
- void MyEFXNode::SetConstraintRotation(const AZ::Quaternion& constraintRotation)
- {
- m_constraintRotation = constraintRotation;
- }
- void MyEFXNode::SetPostRotation(const AZ::Quaternion& postRotation)
- {
- m_postRotation = postRotation;
- }
- void MyEFXNode::SetLimitMin(const AZ::Vector2& limitMin)
- {
- m_limitMin = limitMin;
- }
- void MyEFXNode::SetLimitMax(const AZ::Vector2& limitMax)
- {
- m_limitMax = limitMax;
- }
- void MyEFXNode::SetFollowSpeed(float followSpeed)
- {
- m_followSpeed = followSpeed;
- }
- void MyEFXNode::SetTwistAxis(ConstraintTransformRotationAngles::EAxis twistAxis)
- {
- m_twistAxis = twistAxis;
- }
- void MyEFXNode::SetLimitsEnabled(bool limitsEnabled)
- {
- m_limitsEnabled = limitsEnabled;
- }
- void MyEFXNode::SetSmoothingEnabled(bool smoothingEnabled)
- {
- m_smoothing = smoothingEnabled;
- }
- void MyEFXNode::Reflect(AZ::ReflectContext* context)
- {
- AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
- if (!serializeContext)
- {
- return;
- }
- serializeContext->Class<MyEFXNode, AnimGraphNode>()
- ->Version(2)
- ->Field("targetNodeName", &MyEFXNode::m_targetNodeName)
- ->Field("startNodeName", &MyEFXNode::m_startNodeName)
- ->Field("endNodeName", &MyEFXNode::m_endNodeName)
- ->Field("postRotation", &MyEFXNode::m_postRotation)
- ->Field("limitsEnabled", &MyEFXNode::m_limitsEnabled)
- ->Field("limitMin", &MyEFXNode::m_limitMin)
- ->Field("limitMax", &MyEFXNode::m_limitMax)
- ->Field("constraintRotation", &MyEFXNode::m_constraintRotation)
- ->Field("twistAxis", &MyEFXNode::m_twistAxis)
- ->Field("smoothing", &MyEFXNode::m_smoothing)
- ->Field("followSpeed", &MyEFXNode::m_followSpeed);
- AZ::EditContext* editContext = serializeContext->GetEditContext();
- if (!editContext)
- {
- return;
- }
- auto root = editContext->Class<MyEFXNode>("MyEFXNode", "MyEFXNode At attributes");
- root->ClassElement(AZ::Edit::ClassElements::EditorData, "")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, "")
- ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
- ->DataElement(AZ_CRC("ActorNode", 0x35d9eb50), &MyEFXNode::m_targetNodeName, "Node", "The node to apply the lookat to. For example the head.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &MyEFXNode::Reinit)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
- ->DataElement(AZ_CRC("ActorNode", 0x35d9eb50), &MyEFXNode::m_startNodeName, "StartNode", "The node to apply the lookat to. For example the head.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &MyEFXNode::Reinit)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
- ->DataElement(AZ_CRC("ActorNode", 0x35d9eb50), &MyEFXNode::m_endNodeName, "EndNode", "The node to apply the lookat to. For example the head.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &MyEFXNode::Reinit)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
- ->DataElement(AZ::Edit::UIHandlers::Default, &MyEFXNode::m_postRotation, "Post rotation", "The relative rotation applied after solving.");
- root->ClassElement(AZ::Edit::ClassElements::Group, "Rotation Limits")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
- ->DataElement(AZ::Edit::UIHandlers::Default, &MyEFXNode::m_limitsEnabled, "Enable limits", "Enable rotational limits?")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
- ->DataElement(AZ::Edit::UIHandlers::Default, &MyEFXNode::m_limitMin, "Yaw/pitch min", "The minimum rotational yaw and pitch angle limits, in degrees.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &MyEFXNode::GetLimitWidgetsVisibility)
- ->Attribute(AZ::Edit::Attributes::Min, AZ::Vector2(-90.0f, -90.0f))
- ->Attribute(AZ::Edit::Attributes::Max, AZ::Vector2(90.0f, 90.0f))
- ->DataElement(AZ::Edit::UIHandlers::Default, &MyEFXNode::m_limitMax, "Yaw/pitch max", "The maximum rotational yaw and pitch angle limits, in degrees.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &MyEFXNode::GetLimitWidgetsVisibility)
- ->Attribute(AZ::Edit::Attributes::Min, AZ::Vector2(-90.0f, -90.0f))
- ->Attribute(AZ::Edit::Attributes::Max, AZ::Vector2(90.0f, 90.0f))
- ->DataElement(AZ::Edit::UIHandlers::Default, &MyEFXNode::m_constraintRotation, "Constraint rotation", "A rotation that rotates the constraint space.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &MyEFXNode::GetLimitWidgetsVisibility)
- ->DataElement(AZ::Edit::UIHandlers::ComboBox, &MyEFXNode::m_twistAxis, "Roll axis", "The axis used for twist/roll.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &MyEFXNode::GetLimitWidgetsVisibility);
- root->ClassElement(AZ::Edit::ClassElements::Group, "Smoothing")
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
- ->DataElement(AZ::Edit::UIHandlers::Default, &MyEFXNode::m_smoothing, "Enable smoothing", "Enable rotation smoothing, which is controlled by the follow speed setting.")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
- ->DataElement(AZ::Edit::UIHandlers::Default, &MyEFXNode::m_followSpeed, "Follow speed", "The speed factor at which to follow the goal. A value near zero meaning super slow and a value of 1 meaning instant following.")
- ->Attribute(AZ::Edit::Attributes::Visibility, &MyEFXNode::GetFollowSpeedVisibility)
- ->Attribute(AZ::Edit::Attributes::Min, 0.05f)
- ->Attribute(AZ::Edit::Attributes::Max, 1.0);
- }
- } // namespace EMotionFX
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement