/*
 * Skinning plugin
 */

/******************************************
 *                                        *
 *    RenderWare(TM) Graphics Library     *
 *                                        *
 ******************************************/

/*
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd.
 * or Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. and Canon Inc. will not, under any
 * circumstances, be liable for any lost revenue or other damages
 * arising from the use of this file.
 *
 * Copyright (c) 1998. Criterion Software Ltd.
 * All Rights Reserved.
 */

/***************************************************************************
 *                                                                         *
 * Module  : rpskin.h                                                      *
 *                                                                         *
 * Purpose : Skinned animation                                             *
 *                                                                         *
 **************************************************************************/

#ifndef RPSKIN_H
#define RPSKIN_H

/**
 * \defgroup rpskinp2 RpSkin
 * \ingroup rpplugin
 *
 * Skin Plugin for RenderWare.
 */

/****************************************************************************
 Includes
 */

#include <rpcriter.h>         /* Note: each vendor can choose their own method for
                                * allocation of unique ID's. This file defines
                                * the ID's used by Criterion.
                                */
#include <rpskin.rpe>          /* automatically generated header file */

#include <rwcore.h>
#include <rpworld.h>
#include <rphanim.h>

#define RPSKINLEGACY

typedef struct RpSkinVector RpSkinVector;
typedef struct RpSkinFrame RpSkinFrame;
typedef struct RpSkinBoneInfo RpSkinBoneInfo;
typedef struct RpSkinAnim RpSkinAnim;
typedef struct RpSkinSkeleton RpSkinSkeleton;

/**
 * \ingroup rpskinp2
 * \typedef RpSkinSkeletonCallBack
 * This typedef defines a call-back function for use with the \ref RpSkinSkeletonSetAnimCallBack and
 * \ref RpSkinSkeletonSetAnimLoopCallBack functions.
 *
 * \param  pSkinSkeleton
 * A pointer to the SkinSkeleton structure.
 *
 * \param  pData   User-defined data.
 * You can use this to pass your own data
 * structure(s) to the call-back function.
 *
 * \see RpSkinSkeletonSetAnimCallBack
 * \see RpSkinSkeletonSetAnimLoopCallBack
 *
 */
typedef RpSkinSkeleton * (*RpSkinSkeletonCallBack) (RpSkinSkeleton * pSkinSkeleton,
                                                    void *pData);
typedef struct RwMatrixWeights RwMatrixWeights;
typedef struct RpSkin RpSkin;



/* A 4-element vector for VU0 calculations */

struct RpSkinVector
{
    RwReal              x, y, z, w;
};



/*
 * An animation frame - basically a quaternion and a translation, plus a
 * time.
 *
 * An animation is basically an array of these, presented in the order
 * they are needed to animate forwards through time.
 *
 * The prevFrame pointers link all of the frames for a particular bone
 * backwards through time in a list - less efficient, D-cache-wise, but
 * the majority of anims will play forwards, typically
 *
 * For example, a 3 boned animation, with keyframes at the following times:
 *
 *  Bone 1: 0.0, 1.0, 2.0, 3.0
 *  Bone 2: 0.0, 3.0
 *  Bone 3: 0.0, 2.0, 2.5, 3.0
 *
 * They'd be presented in an RpSkinAnim frame list like this:
 *
 *  B1,0.0 B2,0.0 B3,0.0 B1,1.0, B2,3.0, B3,2.0, B1,2.0, B1,3.0, B3,2.5 B3,3.0
 *
 * Each bone MUST start at time = 0.0, and each bone must terminate with
 * a frame at time = duration of animation.
*/

/**
 * \ingroup rpskinp2
 * \struct RpSkinFrame
 *  A structure representing a bone keyframe.
 */

struct RpSkinFrame
{
    RtQuat          q; /**< Quaternion rotation at keyframe  */
    RwV3d               t; /**< translation at keyframe  */
    RwReal              time; /**< Time at keyframe  */
    RpSkinFrame        *prevFrame; /**< Previous keyframe for particular bone */
};



/* Flags for BoneInfos */

#define rpSKINPOPPARENTMATRIX    0x01
#define rpSKINPUSHPARENTMATRIX   0x02
#define rpSKINANIMATEVERTEXGROUP 0x08

/**
 * \ingroup rpskinp2
 * \struct RpSkinBoneInfo
 *  The hierarchy of a skin's bones is defined by an array of
 * RpSkinBoneInfo structures.
 *
 * These structures hold the "original" bone-to-skin matrix (ie the
 * matrix inverse bone matrix in the pose held in the skin's geometry).
 *
 * The hierarchy is defined by running through the bone array in order,
 * pushing the parent-bone's matrix whenever a child is reached that has
 * more than one sibling, and popping the parent matrix when a "leaf"
 * bone is encountered.
 *
 */

struct RpSkinBoneInfo
{
    RwMatrix            boneToSkinMat; /**< Bone-space to object space matrix  */
    RwInt32             boneTag; /**< User defined tag for this bone  */
    RwInt32             boneIndex; /**< Array index of bone  */
    RwInt32             flags; /**< Matrix push/pop flags  */
    RwFrame *           pFrame; /**< Pointer to an RwFrame that has been attached to
                                    *  a bone via \ref RpSkinAttachFrameToBone() */
};


/**
 * \ingroup rpskinp2
 * \struct RpSkinAnim
 *  A skin animation consists of an array of RpSkinFrame
 * structures that define it, as well as some flags and a duration.
 */

struct RpSkinAnim
{
    RwInt32             numFrames; /**< Number of keyframes in the animation  */
    RwInt32             flags; /**< Unused. Set from the flags parameter of RpSkinAnimCreate */
    RwReal              duration; /**< Duration of animation in seconds */
    RpSkinFrame        *pFrames; /**< Pointer to the skin animation frames  */
};



#define rpSKINSKELETONGETINTERPFRAME( pSkel )   \
    ( (RpSkinFrame *)&(pSkel[1]) )

#define rpSKINSKELETONGETINTERPFRAME1( pSkel )  \
    ( &( ( (RpSkinFrame *)&(pSkel[1]) )[ pSkel->numBones ] ) )

#define rpSKINSKELETONGETINTERPFRAME2( pSkel )  \
    ( &( ( (RpSkinFrame *)&(pSkel[1]) )[ pSkel->numBones * 2 ] ) )

/**
 * \ingroup rpskinp2
 * \struct RpSkinSkeleton
 *  An RpSkinSkeleton is used to "play back" an animation
 * - it holds the interpolated frame data for the current state of an animation
 * concatenated on the end of the structure.
 *
 * The rpSKINSKELETONGETFRAME() macro can be used to access the current
 * interpolated data, for the current time or to write to this data to override
 * it with procedural animation
 */

struct RpSkinSkeleton
{
    RwInt32             numBones; /**< Number of bones in skeleton  */
    RpSkinAnim         *pCurrentAnim; /**< Current skin animation applied to skeleton  */
    RwReal              currentTime; /**< Current animation time  */
    RpSkinFrame        *pNextFrame; /**< Next skin frame to be played  */
    RpSkinSkeletonCallBack pAnimCallBack; /**< Animation callback function pointer  */
    void               *pAnimCallBackData; /**< Animation callback function user data  */
    RwReal              animCallBackTime; /**< Trigger time for callback function  */
    RpSkinSkeletonCallBack pAnimLoopCallBack; /**< Animation loop callback function pointer  */
    void               *pAnimLoopCallBackData; /**< Animation loop callback function data  */
    RwMatrix           *pMatrixArray; /**< Pointer to  bone matrices*/
    void               *pMatrixArrayUnaligned; /**< Pointer to memory used for bone matrices
                                                * from which the aligned pMatrixArray is allocated */
};


/**
 * \ingroup rpskinp2
 * \struct RwMatrixWeights
 * A structure for defining up to four matrix weights per vertex.
 * Not all entries need to be used, (but see the note below).
 *
 * \note
 * Values should be sorted, such that any zero (0.0) entries appear
 * after the valid weights. Any weights that appear after a zero entry
 * will be ignored.
 *
 * \see RpSkinCreate
 */

struct RwMatrixWeights
{
    RwReal              w0;     /**< The first matrix weight */
    RwReal              w1;     /**< The second matrix weight */
    RwReal              w2;     /**< The third matrix weight */
    RwReal              w3;     /**< The fourth matrix weight */
};

/**
 * \ingroup rpskinp2
 * \struct RpSkin
 *  A skin has an array of bones that define its structure,
 * a skeleton that defines its current animation state and other data that
 * is used whilst rendering.
 */

struct RpSkin
{
    RwInt32             numBones; /**< Number of bones in skin  */
    RpSkinBoneInfo     *pBoneInfo; /**< Array of bone data  */
    void               *pBoneInfoUnaligned; /**< Pointer to memory used for bone data
                                               * from which the aligned pBoneInfo array is allocated */
    RwInt32             totalVertices; /**< Number of vertices in skin geometry   */
    RpGeometry         *pGeometry; /**< Pointer to skin geometry  */
    RpSkinSkeleton     *pCurrentSkeleton; /**< Current skeleton attached to skin  */
    RpHAnimHierarchy   *pCurrentHierarchy; /**< Current hierarchy used by skin  */
    RwUInt32           *pMatrixIndexMap; /**< Per-vertex matrix indices   */
    RwMatrixWeights    *pMatrixWeightsMap; /**< Per-vertex matrix weights  */
    void               *pPlatformData; /**< Platform specific data - PS2 NULL, D3D original geometry  */
};

#if (!defined(RpSkinAssign))
#define RpSkinAssign(_target, _source)        \
    ( *(_target) = *(_source) )
#endif /* (!defined(RpSkinAssign)) */

/*--- Plugin API Functions ---*/

#ifdef    __cplusplus
extern              "C"
{
#endif                          /* __cplusplus */

/* RpBone/RpAnim translation functions */

extern RpAtomic    *
RpSkinCreateAtomicFromBoneClump(RpClump * pClump);

extern RpSkinAnim  *
RpSkinExtractAnimFromClumpSequence(RpClump * pClump,
                                   RwChar * pSequenceName);

/* Skin creation */

/* Create an "empty" skin and attach it to an atomic  */
extern RpSkin      *
RpSkinCreate(RpAtomic * pAtomic,
             RwInt32 numBones,
             RwMatrixWeights *
             pVertexWeights,
             RwUInt32 * pVertexIndices,
             RwMatrix * pInverseMatrices,
             RwUInt32 * pFlagArray);

extern RpSkin *
RpSkinDestroy(RpSkin * pSkin);

/* Return the skin currently attached to an atomic */
extern RpSkin      *
RpSkinAtomicGetSkin(RpAtomic * pAtomic);

/* Set the current animation on a skin. */
extern RwBool
RpSkinSetCurrentAnim(RpSkin * pSkin, RpSkinAnim * pAnim);

/* Set the time of the current animation on a skin */
extern RwBool
RpSkinSetCurrentAnimTime(RpSkin * pSkin, RwReal time);

/* Add some time to the current animation on a skin */
extern RwBool
RpSkinAddAnimTime(RpSkin * pSkin, RwReal time);

extern RwBool
RpSkinSubAnimTime(RpSkin * pSkin, RwReal time);

/* Generic skeleton create/destroy (not necessarily for use with an RpSkin) */

extern RpSkinSkeleton *
RpSkeletonCreate(RwInt32 numBones);

extern RpSkinSkeleton *
RpSkeletonDestroy(RpSkinSkeleton * pSkeleton);

/* Skeleton's play back and interpolate animations */

extern RpSkinSkeleton *
RpSkinSkeletonCreate(RpSkin * pSkin);

extern RpSkinSkeleton *
RpSkinSkeletonDestroy(RpSkin * pSkin);

extern RwBool
RpSkinSetSkeleton(RpSkin * pSkin, RpSkinSkeleton * pSkeleton);

extern RpSkinSkeleton *
RpSkinGetSkeleton(RpSkin * pSkin);

extern RwBool
RpSkinSetHAnimHierarchy(RpSkin * pSkin, RpHAnimHierarchy * pHierarchy);

extern RpHAnimHierarchy *
RpSkinGetHAnimHierarchy(RpSkin * pSkin);

extern RwBool
RpSkinSkeletonSetCurrentAnim(RpSkinSkeleton * pSkin,
                             RpSkinAnim * pAnim);

extern RwBool
RpSkinSkeletonSetCurrentAnimTime(RpSkinSkeleton * pSkin,
                                 RwReal time);

extern RwBool
RpSkinSkeletonAddAnimTime(RpSkinSkeleton * pSkin, RwReal time);

extern RpSkinSkeleton *
RpSkinSkeletonSetAnimCallBack( RpSkinSkeleton * pSkeleton, RpSkinSkeletonCallBack pCallBack, RwReal time, void * pData );

extern RpSkinSkeleton *
RpSkinSkeletonSetAnimLoopCallBack( RpSkinSkeleton * pSkeleton, RpSkinSkeletonCallBack pCallBack, void * pData );

extern RwBool
RpSkinSkeletonSubAnimTime(RpSkinSkeleton * pSkin, RwReal time);

extern RwBool
RpSkinUpdateMatrices(RpSkin * pSkin);

extern RwBool
RpSkinAttachFrameToBone( RpSkin * pSkin, RwInt32 boneIndex, RwFrame * pFrame );

extern RwInt32
RpSkinBoneTagToBoneIndex( RpSkin * pSkin, RwInt32 boneTag );

extern void
RpSkinFrameInterpolate(RpSkinFrame * pOut,
                       RpSkinFrame * pIn1, RpSkinFrame * pIn2,
                       RwReal time);

/* Animations */

extern RpSkinAnim  *
RpSkinAnimCreate(RwInt32 numFrames,
                 RwInt32 flags,
                 RwReal duration);

extern RpSkinAnim  *
RpSkinAnimDestroy(RpSkinAnim * pSkinAnim);

extern RpSkinAnim  *
RpSkinAnimRead(const RwChar * pFilename);

extern RwBool
RpSkinAnimWrite(RpSkinAnim * pSkinAnim,
                const RwChar * pFilename);

extern RpSkinAnim  *
RpSkinAnimStreamRead(RwStream *stream);

extern RwBool
RpSkinAnimStreamWrite(RpSkinAnim * pSkinAnim,
                RwStream *stream);

extern RwInt32
RpSkinAnimStreamGetSize(RpSkinAnim * pSkinAnim);

extern RwBool
RpSkinAnimMakeDelta(RpSkinAnim * pSkinAnim,
                    RwInt32 numBones,
                    RwReal time);

/* Plugin support */

extern RwBool
RpSkinPluginAttach(void);

extern RwBool
RpSkinInitPipeline(void);

extern void
RpSkinFrameBlend(RpSkinFrame * pOut, RpSkinFrame * pIn1,
                 RpSkinFrame * pIn2, RwReal fAlpha);

extern RwBool
RpSkinSkeletonBlend(RpSkinSkeleton * pOutSkeleton,
                    RpSkinSkeleton * pInSkeleton1,
                    RpSkinSkeleton * pInSkeleton2,
                    RwReal alpha);
extern RwBool
RpSkinSkeletonAddTogether(RpSkinSkeleton * pOutSkeleton,
                          RpSkinSkeleton * pInSkeleton1,
                          RpSkinSkeleton * pInSkeleton2);

extern RwBool
RpSkinSkeletonCopy(RpSkinSkeleton * pOutSkeleton,
                   RpSkinSkeleton * pInSkeleton);

extern RwBool
RpSkinInvalidateMatrixCache(void);


#ifdef SKY2_DRVMODEL_H

extern void RpSkinSetPS2Lighting(RxWorldLightingCallBack newLightingFunc);
extern RxPipeline *RpSkinGetPS2AllPipeline(void);

/* Allows instancing of skin information from within PS2Manager */
extern RwBool
RxPipelineNodePS2SkinGenericPS2ManagerInstanceCallBack(void **clusterData,
                                                       RwUInt32 numClusters);
/* LEGACY-SUPPORT define for old function name */
#define RxNodePS2SkinGenericPS2ManagerInstanceCallBack \
        RxPipelineNodePS2SkinGenericPS2ManagerInstanceCallBack

/* Skinning PS2All callbacks, potentially useful to users */
extern RwBool
RxPipelineNodePS2SkinPS2AllObjectSetupCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    RwMatrix **transform,
    RxWorldApplyLightFunc lightingFunc);
extern RwBool
RxPipelineNodePS2SkinPS2AllMatBridgeCallBack(
    RxPS2AllPipeData *ps2AllPipeData);
extern RwBool
RxPipelineNodePS2SkinPS2AllInstanceCallBack(
    RxPS2AllPipeData *ps2AllPipeData,
    void **clusterData,
    RwUInt32 numClusters);

#endif /* SKY2_DRVMODEL_H */

#define RpSkinGetNumBones(skin) \
    (skin->numBones)

#ifdef    __cplusplus
}
#endif                          /* __cplusplus */


#endif                          /* RPSKIN_H */

