【目标】
UE4AnimDynamic的移植
【思路】
1 UE3
- 骨骼控制器是固定开启,而不能在动画中控制是否开启
- 如,播放某个动画时开启,或者某个状态下开启这个节点
- UE4中的图就可以灵活控制节点走向
- UE3中,每个SkeletalMeshComponent有个控制器列表(按照骨骼顺序来存储
- 计算骨骼控制器时,也是按照骨骼顺序来计算的,从父骨骼到子骨骼
2 方案:
- 新建一个骨骼控制器类SkelControlAnimDynamic
【步骤】
1 添加一个EngineClassesSkelControlAnimDynamic.uc
/** * Controller that simulate physic Animation. * * Copyright 2017 Kingsoft Games, Inc. All Rights Reserved. */class SkelControlAnimDynamic extends SkelControlBase native(Anim); cpptext{ // USkelControlBase interface virtual void TickSkelControl(FLOAT DeltaSeconds, USkeletalMeshComponent* SkelComp); virtual void GetAffectedBones(INT BoneIndex, USkeletalMeshComponent* SkelComp, TArray<INT>& OutBoneIndices); virtual void CalculateNewBoneTransforms(INT BoneIndex, USkeletalMeshComponent* SkelComp, TArray<FBoneAtom>& OutBoneTransforms); }enum AnimPhysLinearConstraintType{ Free, Limited,};enum AnimPhysAngularConstraintType{ Angular, Cone};enum AnimPhysTwistAxis{ PT_AxisX, PT_AxisY, PT_AxisZ};struct native AnimPhysConstraintSetup{ /** Whether to limit the linear X axis */ var AnimPhysLinearConstraintType LinearXLimitType; /** Whether to limit the linear Y axis */ var AnimPhysLinearConstraintType LinearYLimitType; /** Whether to limit the linear Z axis */ var AnimPhysLinearConstraintType LinearZLimitType; /** Minimum linear movement per-axis (Set zero here and in the max limit to lock) */ var vector LinearAxesMin; /** Maximum linear movement per-axis (Set zero here and in the min limit to lock) */ var vector LinearAxesMax; /** Method to use when constraining angular motion */ var AnimPhysAngularConstraintType AngularConstraintType; /** Axis to consider for twist when constraining angular motion (forward axis) */ var AnimPhysTwistAxis TwistAxis; /** Angle to use when constraining using a cone */ var float ConeAngle; /** X-axis limit for angular motion when using the "Angular" constraint type (Set to 0 to lock, or 180 to remain free) */ var float AngularXAngle_DEPRECATED; /** Y-axis limit for angular motion when using the "Angular" constraint type (Set to 0 to lock, or 180 to remain free) */ var float AngularYAngle_DEPRECATED; /** Z-axis limit for angular motion when using the "Angular" constraint type (Set to 0 to lock, or 180 to remain free) */ var float AngularZAngle_DEPRECATED; var vector AngularLimitsMin; var vector AngularLimitsMax; /** Axis on body1 to match to the angular target direction. */ var AnimPhysTwistAxis AngularTargetAxis; /** Target direction to face for body1 (in body0 local space) */ var vector AngularTarget; /** The values below are calculated on initialisation and used when building the limits */ /** If all axes are locked we can use 3 linear limits instead of the 6 needed for limited axes */ var bool bLinearFullyLocked;};struct native AnimPhysPlanarLimit{ /** When using a driving bone, the plane transform will be relative to the bone transform */ //var FBoneReference DrivingBone; var name DrivingBone; /** Transform of the plane, this is either in component-space if no DrivinBone is specified * or in bone-space if a driving bone is present. */ //FTransform PlaneTransform; var BoneAtom PlaneTransform;};enum ESphericalLimitType{ Inner, Outer};enum AnimPhysSimSpaceType{ Component <ToolTip = "Sim origin is the location/orientation of the skeletal mesh component.">, Actor <ToolTip = "Sim origin is the location/orientation of the actor containing the skeletal mesh component.">, World <ToolTip = "Sim origin is the world origin. Teleporting characters is not recommended in this mode.">, RootRelative <ToolTip = "Sim origin is the location/orientation of the root bone.">, BoneRelative <ToolTip = "Sim origin is the location/orientation of the bone specified in RelativeSpaceBone">,};enum AnimPhysCollisionType{ CoM <DisplayName="CoM", DisplayValue="CoM", ToolTip="Only limit the center of mass from crossing planes.">, CustomSphere <ToolTip="Use the specified sphere radius to collide with planes.">, InnerSphere <ToolTip="Use the largest sphere that fits entirely within the body extents to collide with planes.">, OuterSphere <ToolTip="Use the smallest sphere that wholely contains the body extents to collide with planes.">};struct native AnimPhysSphericalLimit{ structcpptext { FAnimPhysSphericalLimit() : SphereLocalOffset(FVector::ZeroVector) , LimitRadius(0.0f) , LimitType(ESphericalLimitType::Outer) {} } /** Bone to attach the sphere to */ //FBoneReference DrivingBone; var name DrivingBone; /** Local offset for the sphere, if no driving bone is set this is in node space, otherwise bone space */ var vector SphereLocalOffset; /** Radius of the sphere */ var float LimitRadius; /** Whether to lock bodies inside or outside of the sphere */ var ESphericalLimitType LimitType;};/** The space used to run the simulation */var(AnimDynamic) AnimPhysSimSpaceType SimulationSpace;/** When in BoneRelative sim space, the simulation will use this bone as the origin *///var(AnimDynamic) FBoneReference RelativeSpaceBone;var(AnimDynamic) name RelativeSpaceBone;/** Set to true to use the solver to simulate a connected chain */var(AnimDynamic) bool bChain;/** The bone to attach the physics body to, if bChain is true this is the top of the chain *///var(AnimDynamic) FBoneReference BoundBone;var(AnimDynamic) name BoundBone; /** If bChain is true this is the bottom of the chain, otherwise ignored *///FBoneReference ChainEnd;var(AnimDynamic) name ChainEnd;/** Extents of the box to use for simulation */var(AnimDynamic) vector BoxExtents;/** Vector relative to the body being simulated to attach the constraint to */var(AnimDynamic) vector LocalJointOffset;/** Scale for gravity, higher values increase forces due to gravity */var(AnimDynamic) float GravityScale;/** If true the body will attempt to spring back to its initial position */var(AnimDynamic) bool bLinearSpring;/** If true the body will attempt to align itself with the specified angular target */var(AnimDynamic) bool bAngularSpring;/** Spring constant to use when calculating linear springs, higher values mean a stronger spring.*/var(AnimDynamic) float LinearSpringConstant;/** Spring constant to use when calculating angular springs, higher values mean a stronger spring */var(AnimDynamic) float AngularSpringConstant;/** Whether or not wind is enabled for the bodies in this simulation */var(AnimDynamic) bool bEnableWind;/** Scale to apply to calculated wind velocities in the solver */var(AnimDynamic) float WindScale;/** If true, the override value will be used for linear damping */var(AnimDynamic) bool bOverrideLinearDamping;/** Overridden linear damping value */var(AnimDynamic) float LinearDampingOverride;/** If true, the override value will be used for angular damping */var(AnimDynamic) bool bOverrideAngularDamping;/** Overridden angular damping value */var(AnimDynamic) float AngularDampingOverride;/** If true, the override value will be used for the angular bias for bodies in this node. * Angular bias is essentially a twist reduction for chain forces and defaults to a value to keep chains stability * in check. When using single-body systems sometimes angular forces will look like they are "catching-up" with * the mesh, if that's the case override this and push it towards 1.0f until it settles correctly */var(AnimDynamic) bool bOverrideAngularBias;/** Overridden angular bias value * Angular bias is essentially a twist reduction for chain forces and defaults to a value to keep chains stability * in check. When using single-body systems sometimes angular forces will look like they are "catching-up" with * the mesh, if that's the case override this and push it towards 1.0f until it settles correctly */var(AnimDynamic) float AngularBiasOverride;/** If true we will perform physics update, otherwise skip - allows visualisation of the initial state of the bodies */var(AnimDynamic) bool bDoUpdate;/** If true we will perform bone transform evaluation, otherwise skip - allows visualisation of the initial anim state compared to the physics sim */var(AnimDynamic) bool bDoEval;/** Number of update passes on the linear and angular limits before we solve the position of the bodies recommended to be four times the value of NumSolverIterationsPostUpdate */var(AnimDynamic) int NumSolverIterationsPreUpdate;/** Number of update passes on the linear and angular limits after we solve the position of the bodies, recommended to be around a quarter of NumSolverIterationsPreUpdate */var(AnimDynamic) int NumSolverIterationsPostUpdate;/** Data describing the constraints we will apply to the body */var(AnimDynamic) AnimPhysConstraintSetup ConstraintSetup;/** Whether to evaluate planar limits */var(AnimDynamic) bool bUsePlanarLimit;/** List of available planar limits for this node */var(AnimDynamic) array<AnimPhysPlanarLimit> PlanarLimits;/** Whether to evaluate spherical limits */var(AnimDynamic) bool bUseSphericalLimits;/** List of available spherical limits for this node */var(AnimDynamic) array<AnimPhysSphericalLimit> SphericalLimits;/** Resolution method for planar limits */var(AnimDynamic) AnimPhysCollisionType CollisionType;/** Radius to use if CollisionType is set to CustomSphere */var(AnimDynamic) float SphereCollisionRadius;/** An external force to apply to all bodies in the simulation when ticked, specified in world space */var(AnimDynamic) vector ExternalForce;defaultproperties{}
make一下
UE4的
现在填入函数
2添加初始化Physics函数
uc
void InitPhysics(USkeletalMeshComponent* Component); void TermPhysics();
添加一些私有的变量,不需要编辑的,应该是临时变量
问题:
在uc中如何使用C++的F类
只能用Struct来
【实验】:
1.
在uc中写一遍struct,和C++中匹配,不导出到h文件?
但是h文件会找不到申明
2.
除非不导出到h文件,自己写一个h文件来
如果不导出,需要自己派生Serialize函数,自己序列化
3.
在uc中写结构体,导出到h文件,
3 添加结构体
/** * Defines a transform (Position/Orientation) for an anim phys object without scaling */struct native AnimPhysPose{ var vector Position; var quat Orientation; structcpptext { FAnimPhysPose(const FVector& InPosition, const FQuat& InOrient) : Position(InPosition) , Orientation(InOrient) {} FAnimPhysPose() : Position(FVector::ZeroVector) , Orientation(FQuat::Identity) { } FAnimPhysPose Inverse() const { FQuat InverseOrient = Orientation.Inverse(); FVector InversePosition = InverseOrient * Position; return FAnimPhysPose(InversePosition, InverseOrient); } FMatrix Matrix() const { return FBoneAtom(Orientation, Position).ToMatrixNoScale(); } FVector operator*(const FVector& InPoint) const { return Position + (Orientation * InPoint); } FAnimPhysPose operator*(const FAnimPhysPose& InPose) const { return FAnimPhysPose(*this *InPose.Position, Orientation * InPose.Orientation); } FPlane TransformPlane(const FPlane& InPlane) { FVector NewNormal(InPlane.X, InPlane.Y, InPlane.Z); NewNormal = Orientation * NewNormal; return FPlane(NewNormal, InPlane.W - FVector::DotProduct(Position, NewNormal)); } }};
FVector需要添加
FORCEINLINE static FVector CrossProduct(const FVector& A, const FVector& B) { return A ^ B; } FORCEINLINE static float DotProduct(const FVector& A, const FVector& B) { return A | B; }
UnMathFpu.h
/** * Swizzles the 4 components of a vector and returns the result. * * @param Vec Source vector * @param X Index for which component to use for X (literal 0-3) * @param Y Index for which component to use for Y (literal 0-3) * @param Z Index for which component to use for Z (literal 0-3) * @param W Index for which component to use for W (literal 0-3) * @return The swizzled vector */#define VectorSwizzle( Vec, X, Y, Z, W ) MakeVectorRegister( (Vec).V[X], (Vec).V[Y], (Vec).V[Z], (Vec).V[W] )
同理UnMathSSE.h
/** * Creates a vector through selecting two components from each vector via a shuffle mask. * * @param Vec1 Source vector1 * @param Vec2 Source vector2 * @param X Index for which component of Vector1 to use for X (literal 0-3) * @param Y Index for which component to Vector1 to use for Y (literal 0-3) * @param Z Index for which component to Vector2 to use for Z (literal 0-3) * @param W Index for which component to Vector2 to use for W (literal 0-3) * @return The swizzled vector */#define VectorShuffle( Vec1, Vec2, X, Y, Z, W ) _mm_shuffle_ps( Vec1, Vec2, SHUFFLEMASK(X,Y,Z,W) )
FBoneAtomVectorized.h
FORCEINLINE void ToMatrixInternalNoScale( VectorRegister& OutDiagonals, VectorRegister& OutAdds, VectorRegister& OutSubtracts ) const {#if !FINAL_RELEASE && !CONSOLE // Make sure Rotation is normalized when we turn it into a matrix. check( IsRotationNormalized() );#endif const VectorRegister RotationX2Y2Z2 = VectorAdd(Rotation, Rotation); // x2, y2, z2 const VectorRegister RotationXX2YY2ZZ2 = VectorMultiply(RotationX2Y2Z2, Rotation); // xx2, yy2, zz2 // The diagonal terms of the rotation matrix are: // (1 - (yy2 + zz2)) // (1 - (xx2 + zz2)) // (1 - (xx2 + yy2)) const VectorRegister yy2_xx2_xx2 = VectorSwizzle(RotationXX2YY2ZZ2, 1, 0, 0, 0); const VectorRegister zz2_zz2_yy2 = VectorSwizzle(RotationXX2YY2ZZ2, 2, 2, 1, 0); const VectorRegister DiagonalSum = VectorAdd(yy2_xx2_xx2, zz2_zz2_yy2); OutDiagonals = VectorSubtract(VectorOne(), DiagonalSum); // Grouping the non-diagonal elements in the rotation block by operations: // ((x*y2,y*z2,x*z2) + (w*z2,w*x2,w*y2)) and // ((x*y2,y*z2,x*z2) - (w*z2,w*x2,w*y2)) // Rearranging so the LHS and RHS are in the same order as for + // ((x*y2,y*z2,x*z2) - (w*z2,w*x2,w*y2)) // RotBase = x*y2, y*z2, x*z2 // RotOffset = w*z2, w*x2, w*y2 const VectorRegister x_y_x = VectorSwizzle(Rotation, 0, 1, 0, 0); const VectorRegister y2_z2_z2 = VectorSwizzle(RotationX2Y2Z2, 1, 2, 2, 0); const VectorRegister RotBase = VectorMultiply(x_y_x, y2_z2_z2); const VectorRegister w_w_w = VectorReplicate(Rotation, 3); const VectorRegister z2_x2_y2 = VectorSwizzle(RotationX2Y2Z2, 2, 0, 1, 0); const VectorRegister RotOffset = VectorMultiply(w_w_w, z2_x2_y2); // Adds = (RotBase + RotOffset): (x*y2 + w*z2) , (y*z2 + w*x2), (x*z2 + w*y2) // Subtracts = (RotBase - RotOffset) : (x*y2 - w*z2) , (y*z2 - w*x2), (x*z2 - w*y2) OutAdds = VectorAdd(RotBase, RotOffset); OutSubtracts = VectorSubtract(RotBase, RotOffset); } /** * Convert this Transform to a transformation matrix, ignoring its scaling */ FORCEINLINE FMatrix ToMatrixNoScale() const { FMatrix OutMatrix; VectorRegister DiagonalsXYZ; VectorRegister Adds; VectorRegister Subtracts; ToMatrixInternalNoScale( DiagonalsXYZ, Adds, Subtracts ); const VectorRegister DiagonalsXYZ_W0 = VectorSet_W0(DiagonalsXYZ); // OutMatrix.M[0][0] = (1.0f - (yy2 + zz2)); // Diagonal.X // OutMatrix.M[0][1] = (xy2 + wz2); // Adds.X // OutMatrix.M[0][2] = (xz2 - wy2); // Subtracts.Z // OutMatrix.M[0][3] = 0.0f; // DiagonalsXYZ_W0.W const VectorRegister AddX_DC_DiagX_DC = VectorShuffle(Adds, DiagonalsXYZ_W0, 0, 0, 0, 0); const VectorRegister SubZ_DC_DiagW_DC = VectorShuffle(Subtracts, DiagonalsXYZ_W0, 2, 0, 3, 0); const VectorRegister Row0 = VectorShuffle(AddX_DC_DiagX_DC, SubZ_DC_DiagW_DC, 2, 0, 0, 2); // OutMatrix.M[1][0] = (xy2 - wz2); // Subtracts.X // OutMatrix.M[1][1] = (1.0f - (xx2 + zz2)); // Diagonal.Y // OutMatrix.M[1][2] = (yz2 + wx2); // Adds.Y // OutMatrix.M[1][3] = 0.0f; // DiagonalsXYZ_W0.W const VectorRegister SubX_DC_DiagY_DC = VectorShuffle(Subtracts, DiagonalsXYZ_W0, 0, 0, 1, 0); const VectorRegister AddY_DC_DiagW_DC = VectorShuffle(Adds, DiagonalsXYZ_W0, 1, 0, 3, 0); const VectorRegister Row1 = VectorShuffle(SubX_DC_DiagY_DC, AddY_DC_DiagW_DC, 0, 2, 0, 2); // OutMatrix.M[2][0] = (xz2 + wy2); // Adds.Z // OutMatrix.M[2][1] = (yz2 - wx2); // Subtracts.Y // OutMatrix.M[2][2] = (1.0f - (xx2 + yy2)); // Diagonals.Z // OutMatrix.M[2][3] = 0.0f; // DiagonalsXYZ_W0.W const VectorRegister AddZ_DC_SubY_DC = VectorShuffle(Adds, Subtracts, 2, 0, 1, 0); const VectorRegister Row2 = VectorShuffle(AddZ_DC_SubY_DC, DiagonalsXYZ_W0, 0, 2, 2, 3); VectorStoreAligned(Row0, &(OutMatrix.M[0][0])); VectorStoreAligned(Row1, &(OutMatrix.M[1][0])); VectorStoreAligned(Row2, &(OutMatrix.M[2][0])); // OutMatrix.M[3][0] = Translation.X; // OutMatrix.M[3][1] = Translation.Y; // OutMatrix.M[3][2] = Translation.Z; // OutMatrix.M[3][3] = 1.0f; const VectorRegister Row3 = VectorSet_W1(Translation); VectorStoreAligned(Row3, &(OutMatrix.M[3][0])); return OutMatrix; }
4 继续添加结构体,将class 转成struct
struct native AnimPhysState{ var AnimPhysPose Pose;
var vector LinearMomentum; var vector AngularMomentum;
structcpptext { FAnimPhysState(); FAnimPhysState(const FVector& InPosition, const FQuat& InOrient, const FVector& InLinearMomentum, const FVector& InAngularMomentum);
FAnimPhysPose& GetPose() { return Pose; } const FAnimPhysPose& GetPose() const { return Pose; }
FAnimPhysState& GetState() { return *this; } const FAnimPhysState& GetState() const { return *this; } }};
在Object.uc 添加基础类型
/** * An integer vector in 3D space. */struct immutable IntVector{ var() int X, Y, Z;}
需要填充C++代码DevelopmentSrcCoreIncUnMath.h
/*----------------------------------------------------------------------------- FIntVector.-----------------------------------------------------------------------------*//** * Structure for integer vectors in 3-d space. */struct FIntVector{ /** Holds the point's x-coordinate. */ INT32 X; /** Holds the point's y-coordinate. */ INT32 Y; /** Holds the point's z-coordinate. */ INT32 Z;public: /** An int point with zeroed values. */ static const FIntVector ZeroValue; /** An int point with INDEX_NONE values. */ static const FIntVector NoneValue;public: /** * Default constructor (no initialization). */ FIntVector(); /** * Creates and initializes a new instance with the specified coordinates. * * @param InX The x-coordinate. * @param InY The y-coordinate. * @param InZ The z-coordinate. */ FIntVector( INT32 InX, INT32 InY, INT32 InZ ); /** * Constructor * * @param InValue replicated to all components */ explicit FIntVector( INT32 InValue ); /** * Constructor * * @param EForceInit Force init enum */ explicit FORCEINLINE FIntVector( EForceInit );public: /** * Gets specific component of a point. * * @param ComponentIndex Index of point component. * @return const reference to component. */ const INT32& operator()( INT32 ComponentIndex ) const; /** * Gets specific component of a point. * * @param ComponentIndex Index of point component. * @return reference to component. */ INT32& operator()( INT32 ComponentIndex ); /** * Gets specific component of a point. * * @param ComponentIndex Index of point component. * @return const reference to component. */ const INT32& operator[]( INT32 ComponentIndex ) const; /** * Gets specific component of a point. * * @param ComponentIndex Index of point component. * @return reference to component. */ INT32& operator[]( INT32 ComponentIndex ); /** * Compares points for equality. * * @param Other The other int point being compared. * @return true if the points are equal, false otherwise.. */ bool operator==( const FIntVector& Other ) const; /** * Compares points for inequality. * * @param Other The other int point being compared. * @return true if the points are not equal, false otherwise.. */ bool operator!=( const FIntVector& Other ) const; /** * Scales this point. * * @param Scale What to multiply the point by. * @return Reference to this point after multiplication. */ FIntVector& operator*=( INT32 Scale ); /** * Divides this point. * * @param Divisor What to divide the point by. * @return Reference to this point after division. */ FIntVector& operator/=( INT32 Divisor ); /** * Adds to this point. * * @param Other The point to add to this point. * @return Reference to this point after addition. */ FIntVector& operator+=( const FIntVector& Other ); /** * Subtracts from this point. * * @param Other The point to subtract from this point. * @return Reference to this point after subtraction. */ FIntVector& operator-=( const FIntVector& Other ); /** * Assigns another point to this one. * * @param Other The point to assign this point from. * @return Reference to this point after assignment. */ FIntVector& operator=( const FIntVector& Other ); /** * Gets the result of scaling on this point. * * @param Scale What to multiply the point by. * @return A new scaled int point. */ FIntVector operator*( INT32 Scale ) const; /** * Gets the result of division on this point. * * @param Divisor What to divide the point by. * @return A new divided int point. */ FIntVector operator/( INT32 Divisor ) const; /** * Gets the result of addition on this point. * * @param Other The other point to add to this. * @return A new combined int point. */ FIntVector operator+( const FIntVector& Other ) const; /** * Gets the result of subtraction from this point. * * @param Other The other point to subtract from this. * @return A new subtracted int point. */ FIntVector operator-( const FIntVector& Other ) const;public: /** * Gets the maximum value in the point. * * @return The maximum value in the point. */ float GetMax() const; /** * Gets the minimum value in the point. * * @return The minimum value in the point. */ float GetMin() const; /** * Gets the distance of this point from (0,0,0). * * @return The distance of this point from (0,0,0). */ INT32 Size() const; /** * Get a textual representation of this vector. * * @return A string describing the vector. */ FString ToString() const;public: /** * Divide an int point and round up the result. * * @param lhs The int point being divided. * @param Divisor What to divide the int point by. * @return A new divided int point. */ static FIntVector GetDivideAndRoundUp( FIntVector lhs, INT32 Divisor ); /** * Gets the number of components a point has. * * @return Number of components point has. */ static INT32 Num();public: /** * Serializes the Rectangle. * * @param Ar The archive to serialize into. * @param Vector The vector to serialize. * @return Reference to the Archive after serialization. */ friend FArchive& operator<<( FArchive& Ar, FIntVector& Vector ) { return Ar << Vector.X << Vector.Y << Vector.Z; } bool Serialize( FArchive& Ar ) { Ar << *this; return true; }};/* FIntVector inline functions *****************************************************************************/FORCEINLINE FIntVector::FIntVector(){ }FORCEINLINE FIntVector::FIntVector( INT32 InX, INT32 InY, INT32 InZ ) : X(InX) , Y(InY) , Z(InZ){ }FORCEINLINE FIntVector::FIntVector( INT32 InValue ) : X(InValue) , Y(InValue) , Z(InValue){ }FORCEINLINE FIntVector::FIntVector( EForceInit ) : X(0) , Y(0) , Z(0){ }FORCEINLINE const INT32& FIntVector::operator()( INT32 ComponentIndex ) const{ return (&X)[ComponentIndex];}FORCEINLINE INT32& FIntVector::operator()( INT32 ComponentIndex ){ return (&X)[ComponentIndex];}FORCEINLINE const INT32& FIntVector::operator[]( INT32 ComponentIndex ) const{ return (&X)[ComponentIndex];}FORCEINLINE INT32& FIntVector::operator[]( INT32 ComponentIndex ){ return (&X)[ComponentIndex];}FORCEINLINE bool FIntVector::operator==( const FIntVector& Other ) const{ return X==Other.X && Y==Other.Y && Z==Other.Z;}FORCEINLINE bool FIntVector::operator!=( const FIntVector& Other ) const{ return X!=Other.X || Y!=Other.Y || Z!=Other.Z;}FORCEINLINE FIntVector& FIntVector::operator*=( INT32 Scale ){ X *= Scale; Y *= Scale; Z *= Scale; return *this;}FORCEINLINE FIntVector& FIntVector::operator/=( INT32 Divisor ){ X /= Divisor; Y /= Divisor; Z /= Divisor; return *this;}FORCEINLINE FIntVector& FIntVector::operator+=( const FIntVector& Other ){ X += Other.X; Y += Other.Y; Z += Other.Z; return *this;}FORCEINLINE FIntVector& FIntVector::operator-=( const FIntVector& Other ){ X -= Other.X; Y -= Other.Y; Z -= Other.Z; return *this;}FORCEINLINE FIntVector& FIntVector::operator=( const FIntVector& Other ){ X = Other.X; Y = Other.Y; Z = Other.Z; return *this;}FORCEINLINE FIntVector FIntVector::operator*( INT32 Scale ) const{ return FIntVector(*this) *= Scale;}FORCEINLINE FIntVector FIntVector::operator/( INT32 Divisor ) const{ return FIntVector(*this) /= Divisor;}FORCEINLINE FIntVector FIntVector::operator+( const FIntVector& Other ) const{ return FIntVector(*this) += Other;}FORCEINLINE FIntVector FIntVector::operator-( const FIntVector& Other ) const{ return FIntVector(*this) -= Other;}FORCEINLINE FIntVector FIntVector::GetDivideAndRoundUp( FIntVector lhs, INT32 Divisor ){ return FIntVector(DivideAndRoundUp(lhs.X, Divisor), DivideAndRoundUp(lhs.Y, Divisor), DivideAndRoundUp(lhs.Z, Divisor));}FORCEINLINE float FIntVector::GetMax() const{ return Max(Max(X, Y), Z);}FORCEINLINE float FIntVector::GetMin() const{ return Min(Min(X, Y), Z);}FORCEINLINE INT32 FIntVector::Num(){ return 3;}FORCEINLINE INT32 FIntVector::Size() const{ INT64 X64 = (INT64)X; INT64 Y64 = (INT64)Y; INT64 Z64 = (INT64)Z; return INT32(appSqrt(float(X64 * X64 + Y64 * Y64 + Z64 * Z64)));}FORCEINLINE FString FIntVector::ToString() const{ return FString::Printf(TEXT("X=%d Y=%d Z=%d"), X, Y, Z);}FORCEINLINE UINT32 GetTypeHash(const FIntVector& Vector){ return appMemCrc(&Vector,sizeof(FIntVector));}struct FIntVector4{ INT32 X, Y, Z, W; FORCEINLINE FIntVector4() { } FORCEINLINE FIntVector4(INT32 InX, INT32 InY, INT32 InZ, INT32 InW) : X(InX) , Y(InY) , Z(InZ) , W(InW) { } FORCEINLINE explicit FIntVector4(INT32 InValue) : X(InValue) , Y(InValue) , Z(InValue) , W(InValue) { } FORCEINLINE FIntVector4(EForceInit) : X(0) , Y(0) , Z(0) , W(0) { } FORCEINLINE const INT32& operator[](INT32 ComponentIndex) const { return (&X)[ComponentIndex]; } FORCEINLINE INT32& operator[](INT32 ComponentIndex) { return (&X)[ComponentIndex]; } FORCEINLINE bool operator==(const FIntVector4& Other) const { return X==Other.X && Y==Other.Y && Z==Other.Z && W==Other.W; } FORCEINLINE bool operator!=(const FIntVector4& Other) const { return X!=Other.X || Y!=Other.Y || Z!=Other.Z || W!=Other.W; }};struct FUintVector4{ UINT32 X, Y, Z, W; FORCEINLINE FUintVector4() { } FORCEINLINE FUintVector4(UINT32 InX, UINT32 InY, UINT32 InZ, UINT32 InW) : X(InX) , Y(InY) , Z(InZ) , W(InW) { } FORCEINLINE explicit FUintVector4(UINT32 InValue) : X(InValue) , Y(InValue) , Z(InValue) , W(InValue) { } FORCEINLINE FUintVector4(EForceInit) : X(0) , Y(0) , Z(0) , W(0) { } FORCEINLINE const UINT32& operator[](INT32 ComponentIndex) const { return (&X)[ComponentIndex]; } FORCEINLINE UINT32& operator[](INT32 ComponentIndex) { return (&X)[ComponentIndex]; } FORCEINLINE bool operator==(const FUintVector4& Other) const { return X==Other.X && Y==Other.Y && Z==Other.Z && W==Other.W; } FORCEINLINE bool operator!=(const FUintVector4& Other) const { return X!=Other.X || Y!=Other.Y || Z!=Other.Z || W!=Other.W; }};
UnTemplate.h
/** Divides two integers and rounds up */template <class T>FORCEINLINE T DivideAndRoundUp(T Dividend,T Divisor){ return (Dividend + Divisor - 1) / Divisor;}template <class T>FORCEINLINE T DivideAndRoundDown(T Dividend,T Divisor){ return Dividend / Divisor;}
FBoneAtom 需要添加函数
/** Inverts the matrix and then transforms V - correctly handles scaling in this matrix. */ FORCEINLINE FVector InverseTransformPosition(const FVector &V) const; FORCEINLINE FVector InverseTransformPositionNoScale(const FVector &V) const; FORCEINLINE FVector TransformVector(const FVector& V) const; FORCEINLINE FVector TransformVectorNoScale(const FVector& V) const;
FBoneAtom.GetSafeScaleReciprocal
TArray.需要添加拷贝构造
/** * Copy constructor. Use the common routine to perform the copy. * * @param Other The source array to copy. */ FORCEINLINE TArray(const TArray& Other) : ArrayNum( 0 ) , ArrayMax( 0 ) { Copy(Other); }
填充FAnimPhysShape类
//////////////////////////////////////////////////////////////////////////// FAnimPhysShape - Defines a set of vertices that make a shape with volume and CoMFAnimPhysShape::FAnimPhysShape() : Volume(0.0f) , CenterOfMass(0.0f){}FAnimPhysShape::FAnimPhysShape(TArray<FVector>& InVertices, TArray<FIntVector>& InTriangles)// : Vertices(InVertices)// , Triangles(InTriangles){ Vertices = InVertices; Triangles = InTriangles;// Volume = FAnimPhys::CalculateVolume(Vertices, Triangles);// CenterOfMass = FAnimPhys::CalculateCenterOfMass(Vertices, Triangles);}FAnimPhysShape FAnimPhysShape::MakeBox(FVector& Extents){ // A box of zero size will introduce NaNs into the simulation so we stomp it here and log // if we encounter it. if(Extents.SizeSquared() <= SMALL_NUMBER) { //UE_LOG(LogAnimation, Warning, TEXT("AnimDynamics: Attempted to create a simulation box with 0 volume, this introduces NaNs into the simulation. Adjusting box extents to (1.0f,1.0f,1.0f)")); warnf(TEXT("AnimDynamics: Attempted to create a simulation box with 0 volume, this introduces NaNs into the simulation. Adjusting box extents to (1.0f,1.0f,1.0f)")); Extents = FVector(1.0f); } TArray<FVector> Verts; TArray<FIntVector> Tris; Verts.Reserve(8); Tris.Reserve(12); FVector HalfExtents = Extents / 2.0f; // Front Verts Verts.AddItem(FVector(-HalfExtents.X, -HalfExtents.Y, HalfExtents.Z)); Verts.AddItem(FVector(HalfExtents.X, -HalfExtents.Y, HalfExtents.Z)); Verts.AddItem(FVector(HalfExtents.X, -HalfExtents.Y, -HalfExtents.Z)); Verts.AddItem(FVector(-HalfExtents.X, -HalfExtents.Y, -HalfExtents.Z)); // Back Verts Verts.AddItem(FVector(HalfExtents.X, HalfExtents.Y, HalfExtents.Z)); Verts.AddItem(FVector(-HalfExtents.X, HalfExtents.Y, HalfExtents.Z)); Verts.AddItem(FVector(-HalfExtents.X, HalfExtents.Y, -HalfExtents.Z)); Verts.AddItem(FVector(HalfExtents.X, HalfExtents.Y, -HalfExtents.Z)); // Front Tris.AddItem(FIntVector(0, 1, 3)); Tris.AddItem(FIntVector(1, 2, 3)); // Back Tris.AddItem(FIntVector(4, 5, 7)); Tris.AddItem(FIntVector(5, 6, 7)); // Top Tris.AddItem(FIntVector(0, 5, 1)); Tris.AddItem(FIntVector(5, 4, 1)); // Right Tris.AddItem(FIntVector(1, 4, 2)); Tris.AddItem(FIntVector(2, 4, 7)); // Left Tris.AddItem(FIntVector(0, 3, 5)); Tris.AddItem(FIntVector(5, 3, 6)); // Bottom Tris.AddItem(FIntVector(3, 2, 6)); Tris.AddItem(FIntVector(2, 7, 6)); return FAnimPhysShape(Verts, Tris);}void FAnimPhysShape::TransformVerts(FBoneAtom& InTransform){ for (INT Idx = 0; Idx < Vertices.Num(); Idx++) { Vertices(Idx) = InTransform.TransformVector(Vertices(Idx)); }}
问题:继承类怎么处理?
可以用extends
5 添加AnimPhysRigidBody结构体,继承于AnimPhysState
/** * Simple struct holding wind params passed into simulation */struct native AnimPhysWindData{ // Scale for the final velocity var float BodyWindScale; // Current wind speed var float WindSpeed; // World space wind direction var Vector WindDirection; // Mirrors APEX adaption, adds some randomness / billow var float WindAdaption;};/** * A collection of shapes grouped for simulation as a rigid body */struct native AnimPhysRigidBody extends AnimPhysState{ // Mass of this body var float Mass; // 1.0f / Mass var float InverseMass; // Mass free inverse tensor (easier to scale mass) var Matrix InverseTensorWithoutMass; // Full (with mass) inverse tensor in world space var Matrix InverseWorldSpaceTensor; // Inverse Inertia Tensor rotated into world space // Maintained state, Start/Prev/Next var Vector NextPosition; var Quat NextOrientation; var Vector PreviousPosition; var Quat PreviousOrientation; var Vector StartPosition; var Quat StartOrientation; // Whether to use wind forces on this body var bool bWindEnabled; // Per-body wind data (speed, direction etc.) var AnimPhysWindData WindData; // Override angular damping for this body var bool bAngularDampingOverriden; var float AngularDamping; // Override linear damping for this body var bool bLinearDampingOverriden; var float LinearDamping; // Override gravity scale for this body var float GravityScale; // Previous motion state (linear/angular momentum) var AnimPhysState PreviousState; // Body center of mass (CoM of all shapes) var Vector CenterOfMass; // Collision Data (Only how we interact with planes currently) var AnimPhysCollisionType CollisionType; // Radius to use when not using CoM collision mode var float SphereCollisionRadius; // Shapes contained within this body var array<AnimPhysShape> Shapes; structcpptext { FAnimPhysRigidBody(TArray<FAnimPhysShape>& InShapes, const FVector& InPosition); // Spin / Omega for the body FVector Spin(); }};
填充C++
//////////////////////////////////////////////////////////////////////////FAnimPhysRigidBody::FAnimPhysRigidBody(TArray<FAnimPhysShape>& InShapes, const FVector& InPosition) : Mass(1.0f) , InverseMass(1.0f) , InverseTensorWithoutMass(FMatrix::Identity) , InverseWorldSpaceTensor(FMatrix::Identity) , NextOrientation(FQuat::Identity) , PreviousOrientation(FQuat::Identity) , StartOrientation(FQuat::Identity) , bAngularDampingOverriden(false) , AngularDamping(0.0f) , bLinearDampingOverriden(false) , LinearDamping(0.0f) , GravityScale(1.0f)// , Shapes(InShapes){ Shapes = InShapes; StartPosition = InPosition; PreviousPosition = InPosition; NextPosition = InPosition; Pose.Position = InPosition;// CenterOfMass = FAnimPhys::CalculateCenterOfMass(Shapes); Pose.Position += CenterOfMass; StartPosition = Pose.Position; PreviousPosition = Pose.Position; NextPosition = Pose.Position; for (INT Idx = 0; Idx < Shapes.Num(); Idx++) { FAnimPhysShape& CurrentShape = Shapes(Idx); for (INT VertIdx = 0; VertIdx < CurrentShape.Vertices.Num(); VertIdx++ ) { FVector& CurrentVert = CurrentShape.Vertices(VertIdx); CurrentVert -= CenterOfMass; } }// FMatrix InertiaTensor = FAnimPhys::CalculateInertia(Shapes, CenterOfMass); InverseMass = 1.0f / Mass;// InverseTensorWithoutMass = InertiaTensor.Inverse(); InverseWorldSpaceTensor = InverseTensorWithoutMass * InverseMass;}FVector FAnimPhysRigidBody::Spin(){ return InverseWorldSpaceTensor.TransformFVector(AngularMomentum);}
6 FAnimPhys是计算物理相关的类,需要添加到C++中
ue3DevelopmentSrcEngineIncAnimPhysicsSolver.h
#include "EngineAnimClasses.h"#define MIN_flt (1.175494351e-38F) /* min positive value */#define MAX_flt (3.402823466e+38F)#define MIN_dbl (2.2250738585072014e-308) /* min positive value */#define MAX_dbl (1.7976931348623158e+308)/** * Base class for constraint limits */class FAnimPhysLimit{public: FAnimPhysRigidBody* Bodies[2]; FAnimPhysLimit(FAnimPhysRigidBody* InFirstBody, FAnimPhysRigidBody* InSecondBody);};/** * Angular limit, keeps angular torque around an axis within a defined range */class FAnimPhysAngularLimit : public FAnimPhysLimit{public: // Axis of the limit in world space FVector WorldSpaceAxis; // Rotational impulse float Torque; // The required spin required to align the limit float TargetSpin; // Minimum torque this limit can apply float MinimumTorque; // Maximum torque this limit can apply float MaximumTorque; // Cached spin to torque value that is independant of iterations float CachedSpinToTorque; FAnimPhysAngularLimit(); FAnimPhysAngularLimit(FAnimPhysRigidBody* InFirstBody, FAnimPhysRigidBody* InSecondBody, const FVector& InWorldSpaceAxis, float InTargetSpin = 0, float InMinimumTorque = -MAX_flt, float InMaximumTorque = MAX_flt); /** Remove bias added to solve the limit */ void RemoveBias(); /** Solve the limit */ void Iter(float DeltaTime); void UpdateCachedData();};class FAnimPhysLinearLimit : public FAnimPhysLimit{ public: // Position of anchor on first object (in first object local space) FVector FirstPosition; // Position of anchor on second object (in second object local space) FVector SecondPosition; // Normal along which the limit is applied FVector LimitNormal; // Target speed needed to solve the limit float TargetSpeed; // Target speed of the limit without bias (force added just to solve limit) float TargetSpeedWithoutBias; // Minimum force this limit can apply along the normal float MinimumForce; // Maximum force this limit can apply along the normal float Maximumforce; // Sum of impulses applied float SumImpulses; // Cached denominator of the impulse calculation, doesn't change across iterations float InverseInertiaImpulse; // Cached world space position on body 0 FVector WorldSpacePosition0; // Cached world space position on body 1 FVector WorldSpacePosition1; FAnimPhysLinearLimit(); FAnimPhysLinearLimit(FAnimPhysRigidBody* InFirstBody, FAnimPhysRigidBody* InSecondBody, const FVector& InFirstPosition, const FVector& InSecondPosition, const FVector& InNormal = FVector(0.0f, 0.0f, 1.0f), float InTargetSpeed = 0.0f, float InTargetSpeedWithoutBias = 0.0f, const FVector2D& InForceRange = FVector2D(-MAX_flt, MAX_flt)); /** Remove bias added to solve the limit */ void RemoveBias(); /** Solve the limit */ void Iter(float DetlaTime); void UpdateCachedData();};struct FAnimPhysSpring{ // Bodies connected by the spring (Or nullptr for world) FAnimPhysRigidBody* Body0; FAnimPhysRigidBody* Body1; // Extra orientation applied on top of Body0's orientation to the target direction FQuat TargetOrientationOffset; // Target in body0 space for the target axis on body1 to reach FVector AngularTarget; // The axis of body1's orientation to match to the angular target AnimPhysTwistAxis AngularTargetAxis; // Local space anchor positions (in space of Body0 and Body1 respectively) FVector Anchor0; FVector Anchor1; // Spring constant for linear springs float SpringConstantLinear; // Sprint constant for angular springs float SpringConstantAngular; // Whether to apply linear spring force bool bApplyLinear; // Whether to apply angular spring force bool bApplyAngular; /** Applies forces to the bound bodies */ void ApplyForces(float DeltaTime);};/** * Lightweight rigid body motion solver (no collision) used for cosmetic secondary motion in an animation graph * without invoking something heavier like using PhysX to simulate constraints which could be cost prohibitive */class FAnimPhys{public: /** Calculates the volume of a shape * @param InVertices Verts in the shape * @param InTriangles Triangles represented in InVertices */ static float CalculateVolume(const TArray<FVector>& InVertices, const TArray<FIntVector>& InTriangles); /** Calculates the volume of a collection of shapes * @param InShapes Collection of shapes to use */ static float CalculateVolume(const TArray<FAnimPhysShape>& InShapes); /** Calculates the center of mass of a shape * @param InVertices Verts in the shape * @param InTriangles Triangles represented in InVertices */ static FVector CalculateCenterOfMass(const TArray<FVector>& InVertices, const TArray<FIntVector>& InTriangles); /** Calculates the centre of mass of a collection of shapes * @param InShapes Collection of shapes to use */ static FVector CalculateCenterOfMass(const TArray<FAnimPhysShape>& InShapes); /** Calculate the inertia tensor of a shape * @param InVertices Verts in the shape * @param InTriangles Triangles represented in InVertices * @param InCenterOfMass Center of mass of the shape */ static FMatrix CalculateInertia(const TArray<FVector>& InVertices, const TArray<FIntVector>& InTriangles, const FVector& InCenterOfMass); /** Calculate the inertia tensor of a collection of shapes * @param InShapes Shapes to use * @param InCenterOfMass Center of mass of the collection */ static FMatrix CalculateInertia(const TArray<FAnimPhysShape>& InShapes, const FVector& InCenterOfMass); /** Scale the mass and inertia properties of a rigid body * @param InOutRigidBody Body to modify * @param Scale Scale to use */ static void ScaleRigidBodyMass(FAnimPhysRigidBody* InOutRigidBody, float Scale); // scales the mass and all relevant inertial properties by multiplier s /** Apply an impulse to a body * @param InOutRigidBody Body to apply the impulse to * @param InWorldOrientedImpactPoint Location of the impulse * @param InImpulse Impulse to apply */ static void ApplyImpulse(FAnimPhysRigidBody* InOutRigidBody, const FVector& InWorldOrientedImpactPoint, const FVector& InImpulse); /** Performs a physics update on the provided state objects * @param DeltaTime Time for the frame / update * @param Bodies Bodies to integrate * @param LinearLimits Linear limits to apply to the bodies * @param AngularLimits Angular limits to apply to the bodies * @param Springs Linear/Angular springs to apply to the bodies prior to solving * @param NumPreIterations Number of times to iterate the limits before performing the integration * @param NumPostIterations Number of times to iterae the limits after performing the integration */ static void PhysicsUpdate(float DeltaTime, TArray<FAnimPhysRigidBody*>& Bodies, TArray<FAnimPhysLinearLimit>& LinearLimits, TArray<FAnimPhysAngularLimit>& AngularLimits, TArray<FAnimPhysSpring>& Springs, const FVector& GravityDirection, const FVector& ExternalForce, INT32 NumPreIterations = 8, INT32 NumPostIterations = 2); ////////////////////////////////////////////////////////////////////////// // Constraint functions /** Constrain bodies along a provided axis * @param LimitContainer Container to add limits to * @param FirstBody First body in constraint (or nullptr for world) * @param FirstPosition Local position on first body to apply constraint * @param SecondBody Second body in constraint * @param SecondPosition Local position on second body to apply constraint * @param AxisToConstrain Axis to constrain between bodies * @param Limits Limit on the provided axis so that Limits.X(min) < Final Position < Limits.Y(max) * @param MinimumForce Minimum force that can be applied to satisfy the maximum limit (default -MAX_flt) * @param MaximumForce Maximum force that can be applied to satisfy the minimum limit (default MAX_flt) */ static void ConstrainAlongDirection(float DeltaTime, TArray<FAnimPhysLinearLimit>& LimitContainer, FAnimPhysRigidBody *FirstBody, const FVector& FirstPosition, FAnimPhysRigidBody *SecondBody, const FVector& SecondPosition, const FVector& AxisToConstrain, const FVector2D Limits, float MinimumForce = -MAX_flt, float MaximumForce = MAX_flt); /** Constrain bodies together as if fixed or nailed (linear only, bodies can still rotate) * @param LimitContainer Container to add limits to * @param FirstBody First body in constraint (or nullptr for world) * @param FirstPosition Local position on first body to apply constraint * @param SecondBody Second body in constraint * @param SecondPosition Local position on second body to apply constraint */ static void ConstrainPositionNailed(float DeltaTime, TArray<FAnimPhysLinearLimit>& LimitContainer, FAnimPhysRigidBody *FirstBody, const FVector& FirstPosition, FAnimPhysRigidBody *SecondBody, const FVector& SecondPosition); /** Constrain bodies together with linear limits forming a box or prism around the constraint * @param LimitContainer Container to add limits to * @param FirstBody First body in the constraint (or nullptr for world) * @param FirstPosition Local position on first body to apply constraint * @param SecondBody Second body in the constraint * @param SecondPosition Local position on the second body to apply constraint * @param PrismRotation Rotation to apply to the prism axes, only necessary when constraining to world, otherwise the rotation of the first body is used * @param LimitsMin Minimum limits along axes * @param LimitsMax Maximum limits along axes */ static void ConstrainPositionPrismatic(float DeltaTime, TArray<FAnimPhysLinearLimit>& LimitContainer, FAnimPhysRigidBody* FirstBody, const FVector& FirstPosition, FAnimPhysRigidBody* SecondBody, const FVector& SecondPosition, const FQuat& PrismRotation, const FVector& LimitsMin, const FVector& LimitsMax); /** Constraint two bodies together with angular limits, limiting the relative rotation between them. * Note that this allows TWO axes to rotate, the twist axis will always be locked * @param LimitContainer Container to add limits to * @param FirstBody First body in the constraint (or nullptr for world) * @param SecondBody Second body in constraint * @param JointFrame Frame/Rotation of the joint * @param TwistAxis The axis to regard as the twist axis * @param JointLimitMin Minimum limits along each axis (twist axis ignored) * @param JointLimitMax Maximum limits along each axis (twist axis ignored) * @param InJointBias Bias towards second body's forces (1.0f = 100%) */ static void ConstrainAngularRange(float DeltaTime, TArray<FAnimPhysAngularLimit>& LimitContainer, FAnimPhysRigidBody *FirstBody, FAnimPhysRigidBody *SecondBody, const FQuat& JointFrame, AnimPhysTwistAxis TwistAxis, const FVector& JointLimitMin, const FVector& JointLimitMax, float InJointBias); /** Constraints the rotation between two bodies into a cone * @param LimitContainer Container to add limits to * @param FirstBody First body in the constraint (or nullptr for world) * @param Normal0 Normal for the first side of the constraint * @param SecondBody Second body in the constraint * @param Normal1 Normal for the second side of the constraint * @param LimitAngle Angle to limit the cone to * @param InJointBias Bias towards second body's forces (1.0f = 100%) */ static void ConstrainConeAngle(float DeltaTime, TArray<FAnimPhysAngularLimit>& LimitContainer, FAnimPhysRigidBody* FirstBody, const FVector& Normal0, FAnimPhysRigidBody* SecondBody, const FVector& Normal1, float LimitAngle, float InJointBias); // a hinge is a cone with 0 limitangle /** Constrains the position of a body to one side of a plane placed at PlaneTransform (plane normal is Z axis) * @param LimitContainer Container to add limits to * @param Body The body to constrain to the plane * @param PlaneTransform Transform of the plane, with the normal facing along the Z axis of the orientation */ static void ConstrainPlanar(float DeltaTime, TArray<FAnimPhysLinearLimit>& LimitContainer, FAnimPhysRigidBody* Body, const FBoneAtom& PlaneTransform); /** Constrains the position of a body within the requested sphere */ static void ConstrainSphericalInner(float DeltaTime, TArray<FAnimPhysLinearLimit>& LimitContainer, FAnimPhysRigidBody* Body, const FBoneAtom& SphereTransform, float SphereRadius); /** Constrains the position of a body outside of the requested sphere */ static void ConstrainSphericalOuter(float DeltaTime, TArray<FAnimPhysLinearLimit>& LimitContainer, FAnimPhysRigidBody* Body, const FBoneAtom& SphereTransform, float SphereRadius); ////////////////////////////////////////////////////////////////////////// // Spring creation methods static void CreateSpring(TArray<FAnimPhysSpring>& SpringContainer, FAnimPhysRigidBody* Body0, FVector Position0, FAnimPhysRigidBody* Body1, FVector Position1);private: /** Calculate differentiated orientation quaternion * @param InOrientation Orientation of the body * @param InInverseTensor Inverse inertia tensor for the body * @param InAngularMomentum Current angular momentum of the body */ static FQuat DiffQ(const FQuat& InOrientation, const FMatrix &InInverseTensor, const FVector& InAngularMomentum); /** Perform an RK update of the provided orientation * @param InOrient Orientation to update * @param InInverseTensor Inverse inertia tensor of the body * @param InAngularMomentum Angular momentum of the body * @param InDeltaTime Delta time to process the update over */ static FQuat UpdateOrientRK(const FQuat& InOrient, const FMatrix& InInverseTensor, const FVector& InAngularMomentum, float InDeltaTime); /** Initialize the velocity for a given body * @param InBody Body to initialize */ static void InitializeBodyVelocity(float DeltaTime, FAnimPhysRigidBody *InBody, const FVector& GravityDirection); /** Using calculated linear and angular momentum, integrate the position and orientation of a body * @param InBody Body to integrate */ static void CalculateNextPose(float DeltaTime, FAnimPhysRigidBody* InBody); /** Update previous and current pose state * @param InBody Body to update */ static void UpdatePose(FAnimPhysRigidBody* InBody); /** Internal version of constrain angular. ConstrainAngularRange can work out some of these params so wraps this * @param LimitContainer Container to add limits to * @param FirstBody First body in the constraint (or nullptr for world) * @param JointFrame0 Frame/Rotation of the first side of the joint * @param SecondBody Second body in the constraint * @param JointFrame1 Frame/Rotation of the second side of the joint * @param TwistAxis Axis to consider as the twist axis * @param InJointLimitMin Minimum limits for the joint (twist axis ignored, always locked) * @param InJointLimitMax Maximum limits for the joint (twist axis ignored, always locked) */ static void ConstrainAngularRangeInternal(float DeltaTime, TArray<FAnimPhysAngularLimit>& LimitContainer, FAnimPhysRigidBody *FirstBody, const FQuat& JointFrame0, FAnimPhysRigidBody *SecondBody, const FQuat& JointFrame1, AnimPhysTwistAxis TwistAxis, const FVector& InJointLimitMin, const FVector& InJointLimitMax, float InJointBias);};
7 继续添加EngineClassesSkelControlAnimDynamic.uc的成员
问题:函数声明中的宏定义
structcpptext { FAnimPhysAngularLimit(); //FAnimPhysAngularLimit(FAnimPhysRigidBody* InFirstBody, FAnimPhysRigidBody* InSecondBody, const FVector& InWorldSpaceAxis, float InTargetSpin = 0, float InMinimumTorque = -MAX_flt, float InMaximumTorque = MAX_flt); /** Remove bias added to solve the limit */ void RemoveBias(); /** Solve the limit */ void Iter(float DeltaTime); void UpdateCachedData(); }
生成到h文件会编译不通过,因为没有定义
用 const?
有个例子ue3DevelopmentSrcEngineClassesLandscapeInfo.uc
cpptext{... FVector GetLandscapeCenterPos(FLOAT& LengthZ, INT MinX = MAXINT, INT MinY = MAXINT, INT MaxX = MININT, INT MaxY = MININT);
EngineTerrainClasses.h文件中
宏定义在core.h
是枚举
解决:在core.h中定义即可
8 继续添加EngineClassesSkelControlAnimDynamic.uc的成员
/** * Base class for constraint limits */struct native AnimPhysLimit{ var native pointer Bodies[2] {FAnimPhysRigidBody}; structcpptext { FAnimPhysLimit(FAnimPhysRigidBody* InFirstBody, FAnimPhysRigidBody* InSecondBody); }};/** * Angular limit, keeps angular torque around an axis within a defined range */struct native AnimPhysAngularLimit extends AnimPhysLimit{ // Axis of the limit in world space var Vector WorldSpaceAxis; // Rotational impulse var float Torque; // The required spin required to align the limit var float TargetSpin; // Minimum torque this limit can apply var float MinimumTorque; // Maximum torque this limit can apply var float MaximumTorque; // Cached spin to torque value that is independant of iterations var float CachedSpinToTorque; structcpptext { FAnimPhysAngularLimit(); FAnimPhysAngularLimit(FAnimPhysRigidBody* InFirstBody, FAnimPhysRigidBody* InSecondBody, const FVector& InWorldSpaceAxis, float InTargetSpin = 0, float InMinimumTorque = -MAX_flt, float InMaximumTorque = MAX_flt); /** Remove bias added to solve the limit */ void RemoveBias(); /** Solve the limit */ void Iter(float DeltaTime); void UpdateCachedData(); }};struct native AnimPhysLinearLimit extends AnimPhysLimit{ // Position of anchor on first object (in first object local space) var Vector FirstPosition; // Position of anchor on second object (in second object local space) var Vector SecondPosition; // Normal along which the limit is applied var Vector LimitNormal; // Target speed needed to solve the limit var float TargetSpeed; // Target speed of the limit without bias (force added just to solve limit) var float TargetSpeedWithoutBias; // Minimum force this limit can apply along the normal var float MinimumForce; // Maximum force this limit can apply along the normal var float Maximumforce; // Sum of impulses applied var float SumImpulses; // Cached denominator of the impulse calculation, doesn't change across iterations var float InverseInertiaImpulse; // Cached world space position on body 0 var Vector WorldSpacePosition0; // Cached world space position on body 1 var Vector WorldSpacePosition1; structcpptext { FAnimPhysLinearLimit(); FAnimPhysLinearLimit(FAnimPhysRigidBody* InFirstBody, FAnimPhysRigidBody* InSecondBody, const FVector& InFirstPosition, const FVector& InSecondPosition, const FVector& InNormal = FVector(0.0f, 0.0f, 1.0f), float InTargetSpeed = 0.0f, float InTargetSpeedWithoutBias = 0.0f, const FVector2D& InForceRange = FVector2D(-MAX_flt, MAX_flt)); /** Remove bias added to solve the limit */ void RemoveBias(); /** Solve the limit */ void Iter(float DetlaTime); void UpdateCachedData(); }};struct native AnimPhysSpring{ // Bodies connected by the spring (Or nullptr for world) var native pointer Body0 {FAnimPhysRigidBody}; var native pointer Body1 {FAnimPhysRigidBody}; // Extra orientation applied on top of Body0's orientation to the target direction var Quat TargetOrientationOffset; // Target in body0 space for the target axis on body1 to reach var Vector AngularTarget; // The axis of body1's orientation to match to the angular target var AnimPhysTwistAxis AngularTargetAxis; // Local space anchor positions (in space of Body0 and Body1 respectively) var Vector Anchor0; var Vector Anchor1; // Spring constant for linear springs var float SpringConstantLinear; // Sprint constant for angular springs var float SpringConstantAngular; // Whether to apply linear spring force var bool bApplyLinear; // Whether to apply angular spring force var bool bApplyAngular; structcpptext { /** Applies forces to the bound bodies */ void ApplyForces(float DeltaTime); }};...// Cached timestep from the update phase (needed in evaluate phase)var transient float NextTimeStep;// Current amount of time debtvar transient float TimeDebt;// Current time dilationvar transient float CurrentTimeDilation;// Cached physics settings. We cache these on initialise to avoid the cost of accessing UPhysicsSettings a lot each framevar transient float MaxPhysicsDeltaTime;var transient float MaxSubstepDeltaTime;//var transient int MaxSubsteps;//////////////////////////////////////////////////////////////////////////// Cached sim space that we last usedvar transient AnimPhysSimSpaceType LastSimSpace;// Active body listvar transient array<AnimPhysLinkedBody> Bodies;// Pointers to bodies that need to be reset to their bound bone.// This happens on LOD change so we don't make the simulation unstable//var transient array<FAnimPhysLinkedBody*> BodiesToReset;var native transient array<pointer> BodiesToReset{FAnimPhysLinkedBody};// Pointers back to the base bodies to pass to the simulation//var transient array<FAnimPhysRigidBody*> BaseBodyPtrs;var native transient array<pointer> BaseBodyPtrs{FAnimPhysRigidBody};// List of current linear limits built for the current framevar transient array<AnimPhysLinearLimit> LinearLimits;// List of current angular limits built for the current framevar transient array<AnimPhysAngularLimit> AngularLimits;// List of spring force generators created for this framevar transient array<AnimPhysSpring> Springs;// Local space offsets for each bodyvar transient array<Vector> JointOffsets;// List of bone references for all bodies in this node//var transient array<FBoneReference> BoundBoneReferences;// Depending on the LOD we might not be runnning all of the bound bodies (for chains)// this tracks the active bodies.var transient array<int> ActiveBoneIndices;// Gravity direction in sim spacevar transient vector SimSpaceGravityDirection;
9 填充USkelControlAnimDynamic.InitPhysics
ChainEnd 用于 连接骨骼控制器的骨骼节点时赋值,不手动编辑
FAnimNode_AnimDynamics.GetBoneTransformInSimSpace
FAnimNode_SkeletalControlBase.EvaluateComponentSpace中
UE3的USkeletalMeshComponent.ApplyControllersForBoneIndex
10 骨骼基础函数FBoneAtom.SetToRelativeTransform
/** * Set current transform and the relative to ParentTransform. * Equates to This = This->GetRelativeTransform(Parent), but saves the intermediate FTransform storage and copy. */FORCEINLINE void FBoneAtom::SetToRelativeTransform(const FBoneAtom& ParentTransform){ // A * B(-1) = VQS(B)(-1) (VQS (A)) // // Scale = S(A)/S(B) // Rotation = Q(B)(-1) * Q(A) // Translation = 1/S(B) *[Q(B)(-1)*(T(A)-T(B))*Q(B)] // where A = this, B = Other#if DEBUG_INVERSE_TRANSFORM FMatrix AM = ToMatrix(); FMatrix BM = ParentTransform.ToMatrix();#endif const FVector SafeRecipScale3D = GetSafeScaleReciprocal(ParentTransform.Scale3D, SMALL_NUMBER); const FQuat InverseRot = ParentTransform.Rotation.Inverse(); Scale3D *= SafeRecipScale3D; Translation = (InverseRot * (Translation - ParentTransform.Translation)) * SafeRecipScale3D; Rotation = InverseRot * Rotation;#if DEBUG_INVERSE_TRANSFORM DebugEqualMatrix(AM * BM.InverseFast());#endif}
DevelopmentSrcCoreIncFBoneAtomVectorized.h
/*** Set current transform and the relative to ParentTransform.* Equates to This = This->GetRelativeTransform(Parent), but saves the intermediate FTransform storage and copy.*/FORCEINLINE void FBoneAtom::SetToRelativeTransform(const FBoneAtom& ParentTransform){ // A * B(-1) = VQS(B)(-1) (VQS (A)) // // Scale = S(A)/S(B) // Rotation = Q(B)(-1) * Q(A) // Translation = 1/S(B) *[Q(B)(-1)*(T(A)-T(B))*Q(B)] // where A = this, B = Other#if DEBUG_INVERSE_TRANSFORM FMatrix AM = ToMatrix(); FMatrix BM = ParentTransform.ToMatrix();#endif checkSlow(ParentTransform.IsRotationNormalized()); // Scale = S(A)/S(B) VectorRegister VSafeScale3D = VectorSet_W0(GetSafeScaleReciprocal(ParentTransform.Scale3D, ScalarRegister(SMALL_NUMBER))); Scale3D = VectorMultiply(Scale3D, VSafeScale3D); //VQTranslation = ( ( T(A).X - T(B).X ), ( T(A).Y - T(B).Y ), ( T(A).Z - T(B).Z), 0.f ); VectorRegister VQTranslation = VectorSet_W0(VectorSubtract(Translation, ParentTransform.Translation)); // Inverse RotatedTranslation VectorRegister VInverseParentRot = VectorQuaternionInverse(ParentTransform.Rotation); VectorRegister VR = VectorQuaternionRotateVector(VInverseParentRot, VQTranslation); // Translation = 1/S(B) Translation = VectorMultiply(VR, VSafeScale3D); // Rotation = Q(B)(-1) * Q(A) Rotation = VectorQuaternionMultiply2(VInverseParentRot, Rotation ); DiagnosticCheckNaN_All(); #if DEBUG_INVERSE_TRANSFORM DebugEqualMatrix(AM * BM.Inverse());#endif}
FBoneAtomVectorized.h
/** * Create a new transform: OutTransform = A * B using the matrix while keeping the scale that's given by A and B * Please note that this operation is a lot more expensive than normal Multiply * * Order matters when composing transforms : A * B will yield a transform that logically first applies A then B to any subsequent transformation. * * @param OutTransform pointer to transform that will store the result of A * B. * @param A Transform A. * @param B Transform B. */ FORCEINLINE static void MultiplyUsingMatrixWithScale(FBoneAtom* OutTransform, const FBoneAtom* A, const FBoneAtom* B); /** * Create a new transform from multiplications of given to matrices (AMatrix*BMatrix) using desired scale * This is used by MultiplyUsingMatrixWithScale and GetRelativeTransformUsingMatrixWithScale * This is only used to handle negative scale * * @param AMatrix first Matrix of operation * @param BMatrix second Matrix of operation * @param DesiredScale - there is no check on if the magnitude is correct here. It assumes that is correct. * @param OutTransform the constructed transform */ FORCEINLINE static void ConstructTransformFromMatrixWithDesiredScale(const FMatrix& AMatrix, const FMatrix& BMatrix, const VectorRegister& DesiredScale, FBoneAtom& OutTransform); /** * Create a new transform: OutTransform = Base * Relative(-1) using the matrix while keeping the scale that's given by Base and Relative * Please note that this operation is a lot more expensive than normal GetRelativeTrnasform * * @param OutTransform pointer to transform that will store the result of Base * Relative(-1). * @param BAse Transform Base. * @param Relative Transform Relative. */ FORCEINLINE static void GetRelativeTransformUsingMatrixWithScale(FBoneAtom* OutTransform, const FBoneAtom* Base, const FBoneAtom* Relative);
11 USkelControlAnimDynamic.UpdateLimits
void USkelControlAnimDynamic::UpdateLimits(USkeletalMeshComponent* SkelComp){ SCOPE_CYCLE_COUNTER(STAT_AnimDynamicsLimitUpdate); // We're always going to use the same number so don't realloc LinearLimits.Empty(LinearLimits.Num()); AngularLimits.Empty(AngularLimits.Num()); Springs.Empty(Springs.Num()); //const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); //for(int32 ActiveIndex : ActiveBoneIndices) for (INT Idx = 0; Idx < ActiveBoneIndices.Num(); Idx++) { INT32 ActiveIndex = ActiveBoneIndices(Idx); //const FBoneReference& CurrentBoneRef = BoundBoneReferences[ActiveIndex]; FName CurrentBoneName = BoundBoneReferences(ActiveIndex); // If our bone isn't valid, move on //if(!CurrentBoneRef.IsValid(BoneContainer)) if (SkelComp->MatchRefBone(CurrentBoneName) == INDEX_NONE) { continue; } FAnimPhysLinkedBody& ChainBody = Bodies(ActiveIndex); FAnimPhysRigidBody& RigidBody = Bodies(ActiveIndex).RigidBody.PhysBody; FAnimPhysRigidBody* PrevBody = NULL; if (ChainBody.ParentBody) { PrevBody = &ChainBody.ParentBody->PhysBody; } // Get joint transform// FCompactPoseBoneIndex BoneIndex = CurrentBoneRef.GetCompactPoseIndex(BoneContainer);// FTransform BoundBoneTransform = GetBoneTransformInSimSpace(SkelComp, MeshBases, BoneIndex);// // FTransform ShapeTransform = BoundBoneTransform; INT BoneIndex = SkelComp->MatchRefBone(CurrentBoneName); FBoneAtom BoundBoneTransform = GetBoneTransformInSimSpace(SkelComp, BoneIndex); FBoneAtom ShapeTransform = BoundBoneTransform; // Local offset to joint for Body1 FVector Body1JointOffset = LocalJointOffset; if (PrevBody) { // Get the correct offset Body1JointOffset = JointOffsets(ActiveIndex); // Modify the shape transform to be correct in Body0 frame ShapeTransform = FBoneAtom(FQuat::Identity, -Body1JointOffset); } if (ConstraintSetup.bLinearFullyLocked) { // Rather than calculate prismatic limits, just lock the transform (1 limit instead of 6) FAnimPhys::ConstrainPositionNailed(NextTimeStep, LinearLimits, PrevBody, ShapeTransform.GetTranslation(), &RigidBody, Body1JointOffset); } else { if (ConstraintSetup.LinearXLimitType != AnimPhysLinearConstraintType::Free) { FAnimPhys::ConstrainAlongDirection(NextTimeStep, LinearLimits, PrevBody, ShapeTransform.GetTranslation(), &RigidBody, Body1JointOffset, ShapeTransform.GetRotation().GetAxisX(), FVector2D(ConstraintSetup.LinearAxesMin.X, ConstraintSetup.LinearAxesMax.X)); } if (ConstraintSetup.LinearYLimitType != AnimPhysLinearConstraintType::Free) { FAnimPhys::ConstrainAlongDirection(NextTimeStep, LinearLimits, PrevBody, ShapeTransform.GetTranslation(), &RigidBody, Body1JointOffset, ShapeTransform.GetRotation().GetAxisY(), FVector2D(ConstraintSetup.LinearAxesMin.Y, ConstraintSetup.LinearAxesMax.Y)); } if (ConstraintSetup.LinearZLimitType != AnimPhysLinearConstraintType::Free) { FAnimPhys::ConstrainAlongDirection(NextTimeStep, LinearLimits, PrevBody, ShapeTransform.GetTranslation(), &RigidBody, Body1JointOffset, ShapeTransform.GetRotation().GetAxisZ(), FVector2D(ConstraintSetup.LinearAxesMin.Z, ConstraintSetup.LinearAxesMax.Z)); } } if (ConstraintSetup.AngularConstraintType == AnimPhysAngularConstraintType::Angular) {#if WITH_EDITOR // Check the ranges are valid when running in the editor, log if something is wrong if(ConstraintSetup.AngularLimitsMin.X > ConstraintSetup.AngularLimitsMax.X || ConstraintSetup.AngularLimitsMin.Y > ConstraintSetup.AngularLimitsMax.Y || ConstraintSetup.AngularLimitsMin.Z > ConstraintSetup.AngularLimitsMax.Z) { //UE_LOG(LogAnimation, Warning, TEXT("AnimDynamics: Min/Max angular limits for bone %s incorrect, at least one min axis value is greater than the corresponding max."), *BoundBone.BoneName.ToString()); warnf(TEXT("AnimDynamics: Min/Max angular limits for bone %s incorrect, at least one min axis value is greater than the corresponding max."), *BoundBone.ToString()); }#endif // Add angular limits. any limit with 360+ degree range is ignored and left free. FAnimPhys::ConstrainAngularRange(NextTimeStep, AngularLimits, PrevBody, &RigidBody, ShapeTransform.GetRotation(), (AnimPhysTwistAxis)ConstraintSetup.TwistAxis, ConstraintSetup.AngularLimitsMin, ConstraintSetup.AngularLimitsMax, bOverrideAngularBias ? AngularBiasOverride : AnimPhysicsConstants::JointBiasFactor); } else { FAnimPhys::ConstrainConeAngle(NextTimeStep, AngularLimits, PrevBody, BoundBoneTransform.GetRotation().GetAxisX(), &RigidBody, FVector(1.0f, 0.0f, 0.0f), ConstraintSetup.ConeAngle, bOverrideAngularBias ? AngularBiasOverride : AnimPhysicsConstants::JointBiasFactor); } if(PlanarLimits.Num() > 0 && bUsePlanarLimit) { //for(FAnimPhysPlanarLimit& PlanarLimit : PlanarLimits) for (INT Idx = 0; Idx < PlanarLimits.Num(); Idx++) { FAnimPhysPlanarLimit& PlanarLimit = PlanarLimits(Idx); FBoneAtom LimitPlaneTransform = PlanarLimit.PlaneTransform; INT DrivingBoneIndex = SkelComp->MatchRefBone(PlanarLimit.DrivingBone); //if(PlanarLimit.DrivingBone.IsValid(BoneContainer)) if ( DrivingBoneIndex != INDEX_NONE) {// FCompactPoseBoneIndex DrivingBoneIndex = PlanarLimit.DrivingBone.GetCompactPoseIndex(BoneContainer);// // FTransform DrivingBoneTransform = GetBoneTransformInSimSpace(SkelComp, MeshBases, DrivingBoneIndex); FBoneAtom DrivingBoneTransform = GetBoneTransformInSimSpace(SkelComp, DrivingBoneIndex); LimitPlaneTransform *= DrivingBoneTransform; } FAnimPhys::ConstrainPlanar(NextTimeStep, LinearLimits, &RigidBody, LimitPlaneTransform); } } if(SphericalLimits.Num() > 0 && bUseSphericalLimits) { //for(FAnimPhysSphericalLimit& SphericalLimit : SphericalLimits) for (INT Idx = 0; Idx < SphericalLimits.Num(); Idx++) { FAnimPhysSphericalLimit& SphericalLimit = SphericalLimits(Idx); FBoneAtom SphereTransform = FBoneAtom::Identity; SphereTransform.SetTranslation(SphericalLimit.SphereLocalOffset); INT DrivingBoneIndex = SkelComp->MatchRefBone(SphericalLimit.DrivingBone); //if(SphericalLimit.DrivingBone.IsValid(BoneContainer)) if ( DrivingBoneIndex != INDEX_NONE) { //FCompactPoseBoneIndex DrivingBoneIndex = SphericalLimit.DrivingBone.GetCompactPoseIndex(BoneContainer); //FTransform DrivingBoneTransform = GetBoneTransformInSimSpace(SkelComp, MeshBases, DrivingBoneIndex); FBoneAtom DrivingBoneTransform = GetBoneTransformInSimSpace(SkelComp, DrivingBoneIndex); SphereTransform *= DrivingBoneTransform; } switch(SphericalLimit.LimitType) { case ESphericalLimitType::Inner: FAnimPhys::ConstrainSphericalInner(NextTimeStep, LinearLimits, &RigidBody, SphereTransform, SphericalLimit.LimitRadius); break; case ESphericalLimitType::Outer: FAnimPhys::ConstrainSphericalOuter(NextTimeStep, LinearLimits, &RigidBody, SphereTransform, SphericalLimit.LimitRadius); break; default: break; } } } // Add spring if we need spring forces if (bAngularSpring || bLinearSpring) { FAnimPhys::CreateSpring(Springs, PrevBody, ShapeTransform.GetTranslation(), &RigidBody, FVector::ZeroVector); FAnimPhysSpring& NewSpring = Springs.Last(); NewSpring.SpringConstantLinear = LinearSpringConstant; NewSpring.SpringConstantAngular = AngularSpringConstant; NewSpring.AngularTarget = ConstraintSetup.AngularTarget.SafeNormal(); NewSpring.AngularTargetAxis = ConstraintSetup.AngularTargetAxis; NewSpring.TargetOrientationOffset = ShapeTransform.GetRotation(); NewSpring.bApplyAngular = bAngularSpring; NewSpring.bApplyLinear = bLinearSpring; } }}
12 USkelControlAnimDynamic.CalculateNewBoneTransforms.
void USkelControlAnimDynamic::CalculateNewBoneTransforms(INT BoneIndex, USkeletalMeshComponent* SkelComp, TArray<FBoneAtom>& OutBoneTransforms){ check(OutBoneTransforms.Num() == 0); SCOPE_CYCLE_COUNTER(STAT_AnimDynamicsOverall);// int32 RestrictToLOD = CVarRestrictLod.GetValueOnAnyThread();// bool bEnabledForLod = RestrictToLOD >= 0 ? SkelComp->PredictedLODLevel == RestrictToLOD : true;// if (CVarEnableDynamics.GetValueOnAnyThread() == 1 && bEnabledForLod) { if(LastSimSpace != SimulationSpace) { // Our sim space has been changed since our last update, we need to convert all of our // body transforms into the new space. ConvertSimulationSpace(SkelComp, (AnimPhysSimSpaceType)LastSimSpace, (AnimPhysSimSpaceType)SimulationSpace); } // Pretty nasty - but there isn't really a good way to get clean bone transforms (without the modification from // previous runs) so we have to initialize here, checking often so we can restart a simulation in the editor. if (bRequiresInit) { InitPhysics(SkelComp); bRequiresInit = false; } //const FBoneContainer& RequiredBones = MeshBases.GetPose().GetBoneContainer(); while(BodiesToReset.Num() > 0) { FAnimPhysLinkedBody* BodyToReset = BodiesToReset.Pop(); if(BodyToReset /*&& BodyToReset->RigidBody.BoundBone.IsValid(RequiredBones)*/) { INT BoundBoneIndex = SkelComp->MatchRefBone(BodyToReset->RigidBody.BoundBone); if (BoundBoneIndex != INDEX_NONE) { //FTransform BoneTransform = GetBoneTransformInSimSpace(SkelComp, MeshBases, BodyToReset->RigidBody.BoundBone.GetCompactPoseIndex(RequiredBones)); FBoneAtom BoneTransform = GetBoneTransformInSimSpace(SkelComp, BoundBoneIndex); FAnimPhysRigidBody& PhysBody = BodyToReset->RigidBody.PhysBody; PhysBody.Pose.Position = BoneTransform.GetTranslation(); PhysBody.Pose.Orientation = BoneTransform.GetRotation(); PhysBody.LinearMomentum = FVector::ZeroVector; PhysBody.AngularMomentum = FVector::ZeroVector; } } } if (bDoUpdate && NextTimeStep > 0.0f) { // Wind / Force update if(/*CVarEnableWind.GetValueOnAnyThread() == 1 &&*/ bEnableWind) { SCOPE_CYCLE_COUNTER(STAT_AnimDynamicsWindData); //for(FAnimPhysRigidBody* Body : BaseBodyPtrs) for (INT Idx = 0; Idx < BaseBodyPtrs.Num(); Idx++) { FAnimPhysRigidBody* Body = BaseBodyPtrs(Idx); if(SkelComp && GWorld/*SkelComp->GetWorld()*/) { Body->bWindEnabled = bEnableWind; if(Body->bWindEnabled) { //UWorld* World = SkelComp->GetWorld(); FSceneInterface* Scene = GWorld->Scene; // Unused by our simulation but needed for the call to GetWindParameters below float WindMinGust; float WindMaxGust; // Setup wind data Body->bWindEnabled = true; //Scene->GetWindParameters(SkelComp->ComponentToWorld.TransformPosition(Body->Pose.Position), Body->WindData.WindDirection, Body->WindData.WindSpeed, WindMinGust, WindMaxGust); Scene->GetWindParameters(SkelComp->LocalToWorldBoneAtom.TransformPosition(Body->Pose.Position), Body->WindData.WindDirection, Body->WindData.WindSpeed, WindMinGust, WindMaxGust); Body->WindData.WindDirection = SkelComp->LocalToWorldBoneAtom.Inverse().TransformVector(Body->WindData.WindDirection); Body->WindData.WindAdaption = RandRange(0.0f, 2.0f); Body->WindData.BodyWindScale = WindScale; } } } } else { SCOPE_CYCLE_COUNTER(STAT_AnimDynamicsWindData); // Disable wind. //for(FAnimPhysRigidBody* Body : BaseBodyPtrs) for (INT Idx = 0; Idx < BaseBodyPtrs.Num(); Idx++) { FAnimPhysRigidBody* Body = BaseBodyPtrs(Idx); Body->bWindEnabled = false; } } FVector OrientedExternalForce = ExternalForce; if(!OrientedExternalForce.IsNearlyZero()) { OrientedExternalForce = TransformWorldVectorToSimSpace(SkelComp, OrientedExternalForce); } // We don't send any bodies that don't have valid bones to the simulation// TArray<FAnimPhysRigidBody*>& SimBodies = FSimBodiesScratch::Get().SimBodies; static TArray<FAnimPhysRigidBody*> SimBodies; SimBodies.Empty(SimBodies.Num()); //for(int32& ActiveIndex : ActiveBoneIndices) for (INT Idx = 0; Idx < BaseBodyPtrs.Num(); Idx++) { INT32& ActiveIndex = ActiveBoneIndices(Idx); if(BaseBodyPtrs.IsValidIndex(ActiveIndex)) { SimBodies.AddItem(BaseBodyPtrs(ActiveIndex)); } } //if (CVarEnableAdaptiveSubstep.GetValueOnAnyThread() == 1) if (0) { float FixedTimeStep = MaxSubstepDeltaTime * CurrentTimeDilation; // Clamp the fixed timestep down to max physics tick time. // at high speeds the simulation will not converge as the delta time is too high, this will // help to keep constraints together at a cost of physical accuracy FixedTimeStep = Clamp(FixedTimeStep, 0.0f, MaxPhysicsDeltaTime); // Calculate number of substeps we should do. INT32 NumIters = appTrunc((NextTimeStep + (TimeDebt * CurrentTimeDilation)) / FixedTimeStep); NumIters = Clamp(NumIters, 0, MaxSubstep); SET_DWORD_STAT(STAT_AnimDynamicsSubSteps, NumIters); // Store the remaining time as debt for later frames TimeDebt = (NextTimeStep + TimeDebt) - (NumIters * FixedTimeStep); TimeDebt = Clamp(TimeDebt, 0.0f, MaxTimeDebt); NextTimeStep = FixedTimeStep; for (INT32 Iter = 0; Iter < NumIters; ++Iter) { UpdateLimits(SkelComp); FAnimPhys::PhysicsUpdate(FixedTimeStep, SimBodies, LinearLimits, AngularLimits, Springs, SimSpaceGravityDirection, OrientedExternalForce, NumSolverIterationsPreUpdate, NumSolverIterationsPostUpdate); } } else { // Do variable frame-time update const float MaxDeltaTime = MaxPhysicsDeltaTime; NextTimeStep = Min(NextTimeStep, MaxDeltaTime); UpdateLimits(SkelComp); FAnimPhys::PhysicsUpdate(NextTimeStep, SimBodies, LinearLimits, AngularLimits, Springs, SimSpaceGravityDirection, OrientedExternalForce, NumSolverIterationsPreUpdate, NumSolverIterationsPostUpdate); } } if (bDoEval) { SCOPE_CYCLE_COUNTER(STAT_AnimDynamicsBoneEval); //const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); for (INT32 Idx = 0; Idx < BoundBoneReferences.Num(); ++Idx) { //FBoneReference& CurrentChainBone = BoundBoneReferences[Idx]; INT BoneIndex = SkelComp->MatchRefBone(BoundBoneReferences(Idx)); FAnimPhysRigidBody& CurrentBody = Bodies(Idx).RigidBody.PhysBody; // Skip invalid bones //if(!CurrentChainBone.IsValid(BoneContainer)) if (BoneIndex == INDEX_NONE) { continue; } //FCompactPoseBoneIndex BoneIndex = CurrentChainBone.GetCompactPoseIndex(BoneContainer); FBoneAtom NewBoneTransform(CurrentBody.Pose.Orientation, CurrentBody.Pose.Position + CurrentBody.Pose.Orientation.RotateVector(JointOffsets(Idx))); NewBoneTransform = GetComponentSpaceTransformFromSimSpace((AnimPhysSimSpaceType)SimulationSpace, SkelComp, NewBoneTransform); OutBoneTransforms.AddItem(NewBoneTransform); } } // Store our sim space incase it changes LastSimSpace = SimulationSpace; }}
13 节点AnimDynamicSkeletalControl需要初始化物理
- 每次只执行一次
- 在USkeletalMeshComponent.InitSkelControls.中调用
- 需要添加Init虚函数
- 修改属性(ChainEnd、BoundBone)时需要初始化一次
ue3DevelopmentSrcEngineClassesSkelControlBase.uc
virtual void Initialize(FName BoneName) {}
添加USkelControlAnimDynamic.Initialize
void USkelControlAnimDynamic::Initialize(FName BoneName){ BoundBone = BoneName; RequestInitialise();}
在USkeletalMeshComponent.InitSkelControls中调用Initialize
修改时 USkelControlAnimDynamic.PostEditChangeProperty
// Editor modificationvoid USkelControlAnimDynamic::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent){ UProperty* PropertyThatChanged = PropertyChangedEvent.Property; if( GIsEditor && PropertyThatChanged && (PropertyThatChanged->GetFName() == FName(TEXT("ChainEnd"))||PropertyThatChanged->GetFName() == FName(TEXT("BoundBone")) ) ) { RequestInitialise(); } Super::PostEditChangeProperty(PropertyChangedEvent);}
测试运行
两个骨骼
【运行】
3