【目标】

UE4AnimDynamic的移植

【思路】

1 UE3

  • 骨骼控制器是固定开启,而不能在动画中控制是否开启
  • 如,播放某个动画时开启,或者某个状态下开启这个节点
  • UE4中的图就可以灵活控制节点走向
  • UE3中,每个SkeletalMeshComponent有个控制器列表(按照骨骼顺序来存储

  • 计算骨骼控制器时,也是按照骨骼顺序来计算的,从父骨骼到子骨骼



2 方案:

  • 新建一个骨骼控制器类SkelControlAnimDynamic











【步骤】

1 添加一个Engine\Classes\SkelControlAnimDynamic.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++代码\Development\Src\Core\Inc\UnMath.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 CoM
FAnimPhysShape::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++中

ue3\Development\Src\Engine\Inc\AnimPhysicsSolver.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 继续添加\Engine\Classes\SkelControlAnimDynamic.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?

const MAX_AIGROUP_NUMBER = 10;


有个例子\ue3\Development\Src\Engine\Classes\LandscapeInfo.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 继续添加\Engine\Classes\SkelControlAnimDynamic.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 debt
var transient float TimeDebt;
// Current time dilation
var transient float CurrentTimeDilation;
// Cached physics settings. We cache these on initialise to avoid the cost of accessing UPhysicsSettings a lot each frame
var transient float MaxPhysicsDeltaTime;
var transient float MaxSubstepDeltaTime;
//var transient int MaxSubsteps;
//////////////////////////////////////////////////////////////////////////
// Cached sim space that we last used
var transient AnimPhysSimSpaceType LastSimSpace;
// Active body list
var 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 frame
var transient array<AnimPhysLinearLimit> LinearLimits;
// List of current angular limits built for the current frame
var transient array<AnimPhysAngularLimit> AngularLimits;
// List of spring force generators created for this frame
var transient array<AnimPhysSpring> Springs;
// Local space offsets for each body
var 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 space
var 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
}


\Development\Src\Core\Inc\FBoneAtomVectorized.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)时需要初始化一次


\ue3\Development\Src\Engine\Classes\SkelControlBase.uc

    virtual void Initialize(FName BoneName) {}



添加USkelControlAnimDynamic.Initialize 

void USkelControlAnimDynamic::Initialize(FName BoneName)
{
    BoundBone = BoneName;
    RequestInitialise();
}


在USkeletalMeshComponent.InitSkelControls中调用Initialize


修改时 USkelControlAnimDynamic.PostEditChangeProperty 

// Editor modification
void 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






posted on 2017-02-17 15:27  维尔福  阅读(1120)  评论(0编辑  收藏  举报