
/****************************************************************************
 *
 * 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) 2001 Criterion Software Ltd.
 * All Rights Reserved.
 *
 */

/****************************************************************************
 *
 * skinskel.c
 *
 * Copyright (C) 2001 Criterion Technologies.
 *
 * Original author: Matt Thorman
 *
 * Purpose: 
 *
 ****************************************************************************/

#include "rwcore.h"
#include "rpworld.h"
#include "rpskin.h"

#include "skeleton.h"
#include "menu.h"

#include "skinskel.h"

static RpClump             *Clump = NULL;
static RpSkinAnim          *Anim = NULL;
static RpSkin              *Skin = NULL;
static RpClump             *Ball = NULL;
static RwFrame             *BoneFrame = NULL;

static RwReal               ClumpPos = 0.0f; 
//static RwInt32              BoneIndex = 8;
//static RwInt32              LastBoneIndex = 8;
static RwInt32              BoneIndex = 0;
static RwInt32              LastBoneIndex = 0;

static const RwV3d          Xaxis = {1.0f, 0.0f, 0.0f};
static const RwV3d          Yaxis = {0.0f, 1.0f, 0.0f};
static const RwV3d          Zaxis = {0.0f, 0.0f, 1.0f};

/*
 *****************************************************************************
 */
static RpClump *
LoadClump(RwChar *filename)
{
    RwChar      *path;
    RpClump     *clump = NULL;

    path = RsPathnameCreate(filename);
    if (path)
    {
        RwStream    *stream;

        stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, path);
        RsPathnameDestroy(path);
        if( stream )
        {
            if( RwStreamFindChunk(stream, rwID_CLUMP, NULL, NULL) )
            {
                clump = RpClumpStreamRead(stream);
            }

            RwStreamClose(stream, NULL);
        }
    }

    return clump;
}

/*
 *****************************************************************************
 */
static RpSkinAnim *
LoadAnimFile(const RwChar *file)
{
    RpSkinAnim     *anim = NULL;
    RwChar         *path;

    path = RsPathnameCreate(file);
    if (path)
    {
        anim = RpSkinAnimRead(path);
        RsPathnameDestroy(path);
    }

    return anim;
}

/*
 *****************************************************************************
 */
RpAtomic *
GetFirstAtomic(RpAtomic *atomic, void *firstAtomic)
{
    *((RpAtomic **) firstAtomic) = atomic;
    return NULL;
}

/*
 ******************************************************************************
 */
static RwBool
BoneChangeCB(RwBool justCheck)
{
    if (!justCheck)
    {
        RpSkinAttachFrameToBone(Skin, LastBoneIndex, NULL);
        RpSkinAttachFrameToBone(Skin, BoneIndex, BoneFrame);

        LastBoneIndex = BoneIndex;
    }

    return TRUE;
}

/*
 ******************************************************************************
 */
void
SkinSkelMenuOpen()
{
    static RwChar boneLabel[]   = RWSTRING("Attach to bone");
    static RwChar transLabel[] = RWSTRING("Translate");

    static RwBool reset;

    MenuAddSeparator();

    MenuAddEntryInt(boneLabel,
        &BoneIndex, BoneChangeCB, 0, Skin->numBones - 1, 1, NULL);

    MenuAddEntryReal(transLabel,
//        &ClumpPos, NULL, -1.0f, 1.0f, 0.1f);
        &ClumpPos, NULL, -1000.0f, 1000.0f, 10.0f);		// for sully

    return;
}

/*
 ******************************************************************************
 */
RwBool
SkinSkelOpen(RpWorld *world)
{
    RpAtomic    *atomic;
    RwV3d       scl;
	RwV3d translation = {0.0f, 0.0f, 0.0f};

	RwFrame * angleClumpFrame;

    /*
     *  Character
     */
//    RwImageSetPath("./models/angel/");
    RwImageSetPath("./models/sully/");
//    Clump = LoadClump(RWSTRING("./models/angel.dff"));
    Clump = LoadClump(RWSTRING("./models/sully.dff"));
    if (!Clump)
    {
        return FALSE;
    }

    RpClumpForAllAtomics(Clump, GetFirstAtomic, (void *)&atomic);

    Skin = RpSkinAtomicGetSkin(atomic);
    if (!Skin)
    {
        return FALSE;
    }

//    Anim = LoadAnimFile(RWSTRING("./models/angel.ska"));
    Anim = LoadAnimFile(RWSTRING("./models/sully.ska"));
    if (!Anim)
    {
        return FALSE;
    }

	//

	angleClumpFrame = RpClumpGetFrame(Clump);

//  RwFrameRotate(RpClumpGetFrame(Clump), &Yaxis, 180.0f, rwCOMBINEREPLACE);
    RwFrameRotate(angleClumpFrame, &Yaxis, 180.0f, rwCOMBINEREPLACE);

	//
	
	translation.z = 500.0f;
	RwFrameTranslate(angleClumpFrame, &translation, rwCOMBINEPOSTCONCAT);
	
	//

    RpWorldAddClump(world, Clump);

    RpSkinSetSkeleton(Skin, RpSkinSkeletonCreate(Skin));
    RpSkinSetCurrentAnim(Skin, Anim);

    /*
     *  Ball
     */
    Ball = LoadClump(RWSTRING("./models/ball.dff"));
    if (!Ball)
    {
        return FALSE;
    }

	//

    RpWorldAddClump(world, Ball);

    /*
     *  Create a frame to attach to the skeleton. This should be a child
     *  of the clump's frame since it's modelling matrix will be updated in
     *  the local space of the skeleton.
     */
    BoneFrame = RwFrameCreate();
    RpSkinAttachFrameToBone(Skin, BoneIndex, BoneFrame);
    RwFrameAddChild(RpClumpGetFrame(Clump), BoneFrame);

    /*
     *  Scale the ball to a sensible size and attach it to the bone frame
     */
	// scale the ball up for sully

//    scl.x = scl.y = scl.z = 0.01f;
    scl.x = scl.y = scl.z = 3.00f;

    RwFrameScale(RpClumpGetFrame(Ball), &scl, rwCOMBINEREPLACE);
    RwFrameAddChild(BoneFrame, RpClumpGetFrame(Ball));

    return TRUE;
}

/*
 *****************************************************************************
 */
void
SkinSkelClose()
{
    if (Ball)
    {
        RwFrameRemoveChild(BoneFrame);
        RwFrameRemoveChild(RpClumpGetFrame(Ball));
        RwFrameDestroy(BoneFrame);

        RpClumpDestroy(Ball);
    }

    if (Anim)
    {
        RpSkinAnimDestroy(Anim);
        Anim = NULL;
    }

    if (Skin)
    {
        RpSkinSkeletonDestroy(Skin);
        Skin = NULL;
    }

    if (Clump)
    {
        RpWorldRemoveClump(RpClumpGetWorld(Clump), Clump);
        RpClumpDestroy(Clump);
        Clump = NULL;
    }
}

/*
 *****************************************************************************
 */
void
SkinSkelUpdate(RwReal deltaTime)
{
	unsigned int boneIndex;

	//

    if (deltaTime > 0.0f)
    {
        RpSkinSkeleton *skeleton = RpSkinGetSkeleton(Skin);
        RpSkinFrame    *rootIFrame;

        /*
         *  Adding time to the animation on the skeleton sets up
         *  a new array of interpolated keyframes which give the current
         *  positions and orientations of the bones in the skeleton 
         *  hierarchy.
         */
        RpSkinSkeletonAddAnimTime(skeleton, deltaTime); 

        /*
         *  We modify the interpolated keyframe for the root bone 
         *  in the hierarchy to achieve the translation. We could alternatively
         *  extract a translation and use it to move the RwFrame with
         *
         *      RwFrameTranslate(RpClumpGetFrame(Clump), &rootIFrame->t, rwCOMBINEPOSTCONCAT);
         *      rootIFrame->t.x = rootIFrame->t.y = rootIFrame->t.z = 0.0f;  
         */
//      rootIFrame = rpSKINSKELETONGETINTERPFRAME(skeleton);
		
		boneIndex = 0;

		rootIFrame = (rpSKINSKELETONGETINTERPFRAME(skeleton) + boneIndex);
        
        rootIFrame->t.x = ClumpPos;

        /*
         *  Updating the matrices converts the interpolated keyframes to
         *  the bone matrix array.
         */
        RpSkinUpdateMatrices(Skin);
    }

    return;
}

/*
 *****************************************************************************
 */
void
SkinSkelRender()
{
    RpWorldRender(RpClumpGetWorld(Clump));

    return;
}
