• 17011701(UE4AnimDynamic的移植)


    【目标】

    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 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++中

    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?

    const MAX_AIGROUP_NUMBER = 10;


    有个例子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 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
    }


    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 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






  • 相关阅读:
    union 和 union all的区别
    JDBC中PreparedStatement相比Statement的好处
    25个经典的Spring面试问答
    MySQL 事务
    漫谈Linux下的音频问题(转)
    监控 Linux 性能的 18 个命令行工具(转)
    在终端中用默认程序打开文件(转)
    【转】使程序在Linux下后台运行 (关掉终端继续让程序运行的方法)
    Getting Started with Amazon EC2 (1 year free AWS VPS web hosting)
    压缩解压命令小结
  • 原文地址:https://www.cnblogs.com/username/p/6410069.html
Copyright © 2020-2023  润新知