/*======================================================================================================*/
/* Path constraint animation										*/
/*													*/
/* Plugin for EQUINOX-3D										*/
/*													*/
/* AUTHOR:	Gabor Nagy										*/
/* DATE:	1996-Oct-25 22:20:46									*/
/*													*/
/* EQUINOX-3D(TM), 3DPanel(TM) and 3DLib(TM) Copyright (C) 1995 By Gabor Nagy. All rights reserved.	*/
/*======================================================================================================*/
#include <stdio.h>
#include <math.h>

#include <EMalloc.h>
#include <EPlugins.h>


#include <E3D/E3D.h>

#include <E3D/Matrix.h>

#include <E3D/Model.h>

#include <E3D/Panel.h>

#include <E3D/StatusPanel.h>

#include <E3D/TimePanel.h>

#include <E3D/Pick.h>

#include <E3D/Polygon.h>

#include <E3D/Scene.h>

#include <E3D/Spline.h>


static EPlugin*		_Plugin=NULL;

static EguiItem		E3dp_MenuButton = NULL;


enum
{
 E3dCR_PATH_ANIM
};


typedef struct
{
// E3dAnimation part
//
 E3dAnimationCore();

// E3dPathAnimation part
//
 E3dModel*	Model;			// The animated Transform node
 E3dModel*	PathModel;		// Pointer to the Spline's Transform node
 E3dSpline*	PathSpline;		// Pointer to the Spline
 E3dCoordinate	AutoBankFactor;		// Automatic banking in turns
 E3dCoordinate	CameraForwardLooking;	// If the animated Model is a Camera, put its interest point in front automatically
} E3dPathAnimation;



static E3dAnimationClass*	_AnimationClass=NULL;


/*==============================================*/
/* Path animation SetFrame procedure		*/
/*==============================================*/
static void _SetFrameProc(E3dAnimation* LAnimation, double LFrame)
{
 E3dPathAnimation*	LPathAnimation=(E3dPathAnimation*)LAnimation;
 E3dModel*		LModel=LPathAnimation->Model;
 E3d3DPosition		L3DPosition;
 E3dSpline*		LPathSpline=LPathAnimation->PathSpline;
 E3dCoordinate		LT;


// For seamless looping on closed Splines...
//
 if(LPathSpline->Closed) LT=LFrame/(E3dp_TimePanel->EndFrame-E3dp_TimePanel->StartFrame+1.0);
 else LT=LFrame/(E3dp_TimePanel->EndFrame-E3dp_TimePanel->StartFrame);

 E3d_SplineGet3DPosition(LPathSpline, LT, &L3DPosition);

 LModel->Translation=L3DPosition;

 switch(LModel->Type)
 {
  case E3dMDL_LIGHT:
   {
    E3dLight*		LLight;
    E3dLightInfo*	LLightInfo=(E3dLightInfo*)(LModel->Info[0]);

    E3d_ModelHrcRefreshMatrices(LModel);
    if((LLight=LLightInfo->Light)!=NULL)
    {
     LLight->Position.X=LModel->Translation.X;
     LLight->Position.Y=LModel->Translation.Y;
     LLight->Position.Z=LModel->Translation.Z;
     E3d_Scene->Changed|=(E3dCHG_LIGHT|E3dCHG_TRANSFORM);
    }
   }
  break;

  case E3dMDL_CAMERA:
   {
    E3dCameraInfo*	LCameraInfo=(E3dCameraInfo*)(LModel->Info[0]);
    E3dCamera*		LCamera=LCameraInfo->Camera;


    LCamera->Position=L3DPosition;
    LCamera->BankAngle=0.0;
    LCamera->UpVector.X=0.0;
    LCamera->UpVector.Y=1.0;
    LCamera->UpVector.Z=0.0;

// Automatic forward-looking camera
//
    if(LPathAnimation->CameraForwardLooking!=0.0)
    {
     E3d_SplineGet3DPosition(LPathSpline, LT+LPathAnimation->CameraForwardLooking, &L3DPosition);
     LCamera->InterestPoint=L3DPosition;
     LModel->Child->Translation=L3DPosition;
    }

    if(LPathAnimation->AutoBankFactor!=0.0)
    {
    }


    E3d_CameraRefreshMatrices(LCamera);
    E3d_Scene->Changed|=E3dCHG_CAMERA;

    E3d_CallCallbacks(LCamera, LCamera->Callbacks, LCamera->NumOfCallbacks, NULL);
   }
  break;

  case E3dMDL_CAMERA_INTEREST:
   {
    E3dCameraInfo*	LCameraInfo=(E3dCameraInfo*)(LModel->Parent->Info[0]);
    E3dCamera*		LCamera=LCameraInfo->Camera;

    LCamera->InterestPoint=L3DPosition;
    E3d_CameraRefreshMatrices(LCamera);
    E3d_Scene->Changed|=E3dCHG_CAMERA;
   }
  break;

  default:
   E3d_ModelHrcRefreshMatrices(LModel);
   E3d_Scene->Changed|=E3dCHG_TRANSFORM;
  break;
 }

// printf("SetFrame %f\n", LFrame);fflush(stdout);
}


/*==============================================================*/
/* Path animation pick-spline callback				*/
/*==============================================================*/
static void _E3d_PickCallback(E3dPickCallbackStruct* LPickStruct, int LNumOfItems, E3dItem* LPickedItems)
{
 if(LPickStruct->Reason==E3dCR_ABORT)
 {
  return;
 }

 if(LPickStruct->Button==Button3)
 {
  E3dp_PickEnd();
  return;
 }

 switch(LPickStruct->Button)
 {
  case Button1:
   if(LNumOfItems)
   {
    if(LPickedItems[0].Type==E3dITEM_SPLINE)
    {
     E3dSplineItem*	LSplineItem=(E3dSplineItem*)LPickedItems;
     E3dModel**		LSelectedModels;
     unsigned int	LNumOfModels;
     E3dPathAnimation*	LPathAnimation;
     E3dModel*		LPathModel=LSplineItem->Model;
     E3dSpline*		LSpline=LSplineItem->Spline;
     unsigned int	LC;

     E3d_SplineUpdateSegmentLengths(LSpline, 0);



     if((LNumOfModels=E3d_SceneGetSelectedModels(E3d_Scene, &LSelectedModels, FALSE))>0)
     {
      for(LC=0;LC<LNumOfModels;LC++)
      {

       if((LPathAnimation=(E3dPathAnimation*)E3d_SceneAddAnimation(E3d_Scene, _AnimationClass))!=NULL)
       {
	LPathAnimation->Model=LSelectedModels[LC];LSelectedModels[LC]->RefCnt+=1;
        LPathAnimation->PathModel=LPathModel;LPathModel->RefCnt+=1;
        LPathAnimation->PathSpline=LSpline;LSpline->RefCnt+=1;

	LPathAnimation->AutoBankFactor=0.2;
//	LPathAnimation->CameraForwardLooking=0.005;
	LPathAnimation->CameraForwardLooking=0.0;


        _SetFrameProc((E3dAnimation*)LPathAnimation, E3dp_TimePanel->Frame);
       }

      }
      EFree(LSelectedModels);

      E3d_SceneLightsRefreshGL(E3d_Scene);

      LPathModel->Selection=E3dSEL_GEOMETRY;
      LSpline->Selection=E3dGSEL_GEOMETRY;

      E3dp_PrintMessage(0, 3000, "Picked '%s' for path", LPathModel->Name);

      E3dp_Refresh3DWindows(E3dDF_ALL, E3dVM_ALL);
     }


     E3dp_PickEnd();
    }
    else E3dp_PrintMessage(0, 3000, "Pick a Spline for path");
   }
  break;
 }
}


/*======================================*/
/* Path animation			*/
/*======================================*/
void E3d_PathAnim()
{
 E3dModel**	LSelectedModels;
 unsigned int	LNumOfModels;

 if((LNumOfModels=E3d_SceneGetSelectedModels(E3d_Scene, &LSelectedModels, FALSE))>0)
 {
  EFree(LSelectedModels);
printf("Path animation\n");fflush(stdout);

  E3dp_ResetWorkModes();
  if(E3dp_PickRequest(E3dITEM_SPLINE, _E3d_PickCallback, 0, FALSE)==0)
  {
   E3dp_PushMessage(0, 0, "Path animation  LMB: pick path spline   RMB/Esc: end mode");
  }

 }
 else E3dp_PrintMessage(0, 3000, "Select Model(s) to animate");
}


/*======================================*/
/* Menu callback			*/
/*======================================*/
static void E3dMCB_Anim(EguiItem LGUIItem, EPointer LClientData, EPointer LCallData)
{
 switch((int)LClientData)
 {
  case E3dCR_PATH_ANIM:
   E3d_PathAnim();
  break;
 }
}


/*======================================*/
/* Key callback				*/
/*======================================*/
static void E3dKCB_PathAnim(unsigned short LKey, unsigned int LModifiers, int LKeyEventType, EPointer LClientData)
{
 E3dMCB_Anim(NULL, LClientData, NULL);
}



/*======================================*/
/* Entry point of the plugin		*/
/*======================================*/
int Plugin_Init(EPlugin* LPlugin)
{
 E3dAnimationClass	LAnimationClass=
   {
    "Path animation",		// Name
    sizeof(E3dPathAnimation),	// StructSize
    _SetFrameProc,		// SetFrameProc
    NULL,			// FreeProc
    NULL			// Resources
   };


 _Plugin=LPlugin;

 if((_AnimationClass=E3d_AnimationClassRegister(&LAnimationClass))!=NULL)
 {
  E3dp_MenuButton=EGUI_AddPushButton("Menu->Animation", "Simple path animation", '\0', NULL, "J", FALSE, NULL, E3dMCB_Anim, (EPointer)E3dCR_PATH_ANIM);

  E_KeySetCallback("Path anim", (unsigned short)'J', 0, EKeyPRESS, E3dKCB_PathAnim, (EPointer)E3dCR_PATH_ANIM);
  return(0);
 }
 return(-1);
}


/*======================================*/
/* Exit method of the plugin		*/
/*======================================*/
int Plugin_Exit()
{
 if(E3dp_MenuButton) EGUI_DestroyItem(E3dp_MenuButton);

 if(_AnimationClass) E3d_AnimationClassDeactivate(_AnimationClass);

 E_KeyRemoveCallback((unsigned short)'J', 0, EKeyPRESS, E3dKCB_PathAnim, (EPointer)E3dCR_PATH_ANIM);
 return(0);
}
