/*===========================================================================
 *
 * File:	Df_3DSExp.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Friday, March 22, 2002
 *
 * Implementation of 3DS specific export functions for DF 3D objects.
 *
 *=========================================================================*/

	/* Include Files */
#include "df3dexpo.h"
#include "bsa/df3dutil.h"


/*===========================================================================
 *
 * Begin Local Variable Definitions
 *
 *=========================================================================*/
  DEFINE_FILE();
/*===========================================================================
 *		End of Local Variable Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int l_ExportDF3dFaceTo3DS (DFFace, pCallbackData);
 *
 * Local function used as a callback function for the CDF3dObject::ForEachFace()
 * method.  Outputs one face to the current position in the file stream 
 * contained in the callback data pointer.  Returns 0 on success.
 *
 * The faces are output as triangle fans, based on the first point in the
 * face.  This is because Daggerfall stores its faces as non-triangular
 * elements (except for planes with 3 points of course) while the 3DS
 * format stores its faces solely as triangular elements.  The face is
 * output in such a way that it displays correctly as a single polygon in
 * 3DS and not a collection of triangular faces (in general anyways).
 *
 * This version of the face export function should be paired with the
 * l_ExportDF3dPointTo3DS() function. These two function keep the DF method
 * of using the same point for several faces where faces intersect.  This
 * method cannot be used if materials are to be export to the 3DS file
 * as UV texture coordinates cannot be specified for all the faces.
 *
 *=========================================================================*/
int l_ExportDF3dFaceTo3DS (const df3dface_t& DFFace, void* pData) {
  //DEFINE_FUNCTION("l_ExportDF3dFaceTo3DS()");
  df3dexport3ds_t* pCallbackData = (df3dexport3ds_t *) pData;
  int     OffsetDivisor;
  boolean Result;
  long    PointIndex;
  short   Point1;
  short   Point2;
  short   Point3;
   
	/* Ignore if no points in face to export */
  if (DFFace.NumPoints <= 0) return (0);

	/* Initialize variables */
  OffsetDivisor = GetDF3dFacePointOffsetDivisor(pCallbackData->ObjectVersion);
      
	/* Output the first sub-triangle in face */
  Point1 = (short) DFFace.FaceData[0].PointOffset / OffsetDivisor;
  Point2 = (short) DFFace.FaceData[1].PointOffset / OffsetDivisor;
  Point3 = (short) DFFace.FaceData[2].PointOffset / OffsetDivisor;
  Result = pCallbackData->pFile3DS->WriteFace(Point1, Point2, Point3, 6);
  if (!Result) return (-1);

	/* Output remaining sub-triangles in the face */
  for (PointIndex = 2; PointIndex < DFFace.NumPoints-1; PointIndex++) {
    Point2 = (short) DFFace.FaceData[PointIndex  ].PointOffset / OffsetDivisor;
    Point3 = (short) DFFace.FaceData[PointIndex+1].PointOffset / OffsetDivisor;
    Result = pCallbackData->pFile3DS->WriteFace(Point1, Point2, Point3, 2);
    if (!Result) return (-1);
   }
    
  return (0);
 }
/*===========================================================================
 *		End of Function l_ExportDF3dFaceTo3DS()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int l_ExportDF3dFaceTo3DS2 (DFFace, pCallbackData);
 *
 * Local function used as a callback function for the CDF3dObject::ForEachFace()
 * method.  Outputs one face to the current position in the file stream 
 * contained in the callback data pointer.  Returns 0 on success.  Uses
 * and updates the PointIndex data member of the callback data.
 *
 * The faces are output as triangle fans, based on the first point in the
 * face.  This is because Daggerfall stores its faces as non-triangular
 * elements (except for planes with 3 points of course) while the 3DS
 * format stores its faces solely as triangular elements.  The face is
 * output in such a way that it displays correctly as a single polygon in
 * 3DS and not a collection of triangular faces (in general anyways).
 *
 * This version of the face export function should be paired with the
 * l_ExportDF3dPointTo3DS2() function. These two functions use multiple points
 * where faces intersect (instead of a single point).  This method must be
 * used if materials are to be export to the 3DS file as mulitple UV texture
 * coordinates are required for each point on the face.
 *
 *=========================================================================*/
int l_ExportDF3dFaceTo3DS2 (const df3dface_t& DFFace, void* pData) {
  //DEFINE_FUNCTION("l_ExportDF3dFaceTo3DS2()");
  df3dexport3ds_t* pCallbackData = (df3dexport3ds_t *) pData;
  boolean Result;
  short   PointIndex;
  short   Point1;
  short   Point2;
  short   Point3;
   
	/* Ignore if no points in face to export */
  if (DFFace.NumPoints <= 0) return (0);
      
	/* Output the first sub-triangle in face */
  Point1 = (short) pCallbackData->PointIndex;
  Point2 = (short) pCallbackData->PointIndex + 1;
  Point3 = (short) pCallbackData->PointIndex + 2;
  Result = pCallbackData->pFile3DS->WriteFace(Point1, Point2, Point3, 6);
  if (!Result) return (-1);

	/* Output remaining sub-triangles in the face */
  for (PointIndex = 2; PointIndex < DFFace.NumPoints-1; PointIndex++) {
    Point2 = (short) (pCallbackData->PointIndex + PointIndex);
    Point3 = (short) pCallbackData->PointIndex + PointIndex + 1;

    Result = pCallbackData->pFile3DS->WriteFace(Point1, Point2, Point3, 2);
    if (!Result) return (-1);
   }
	
	/* Increase the total point count for the next face output */
  pCallbackData->PointIndex += DFFace.NumPoints;  
  return (0);
 }
/*===========================================================================
 *		End of Function l_ExportDF3dFaceTo3DS2()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int l_ExportDF3dPointTo3DS (DFPoint, pCallbackData);
 *
 * Local function used as a callback function for the CDF3dObject::ForEachPoint()
 * method.  Outputs one point to the current position in the file stream 
 * contained in the callback data pointer.  Returns 0 on success.
 *
 * NOTE: Y-Z coordinates swapped, Z coordinate negated.  Points scaled down
 * by DF3D_3DS_FACTOR.
 *
 * This version of the points export function should be paired with the
 * l_ExportDF3dFaceTo3DS() function. These two function keep the DF method
 * of using the same point for several faces where faces intersect.  This
 * method cannot be used if materials are to be export to the 3DS file
 * as UV texture coordinates cannot be specified for all the faces.
 *
 *=========================================================================*/
int l_ExportDF3dPointTo3DS (const df3dpoint_t& DFPoint, void* pData) {
  //DEFINE_FUNCTION("l_ExportDF3dPointTo3DS()");
  df3dexport3ds_t* pCallbackData = (df3dexport3ds_t *) pData;
  boolean Result;
  float   X, Y, Z;
      
	/* Get the base point coordinates */
  X = (float)  DFPoint.X / DF3D_3DS_FACTOR;
  Z = (float) -DFPoint.Y / DF3D_3DS_FACTOR;
  Y = (float)  DFPoint.Z / DF3D_3DS_FACTOR;
 
	/* Transform the point if required and output */
  if (pCallbackData->pMeshMatrix != NULL) {
    const float* pMeshMatrix = pCallbackData->pMeshMatrix;
    float	 X1, Y1, Z1;
    
    X1 = pMeshMatrix[0]*X + pMeshMatrix[3]*Y + pMeshMatrix[6]*Z + pMeshMatrix[9];
    Y1 = pMeshMatrix[1]*X + pMeshMatrix[4]*Y + pMeshMatrix[7]*Z + pMeshMatrix[10];
    Z1 = pMeshMatrix[2]*X + pMeshMatrix[5]*Y + pMeshMatrix[8]*Z + pMeshMatrix[11];
    Result = pCallbackData->pFile3DS->WritePoint(X1, Y1, Z1);
   }
  else {
    Result = pCallbackData->pFile3DS->WritePoint(X, Y, Z);
   }
    
  return (Result ? 0 : -1);
 }
/*===========================================================================
 *		End of Function l_ExportDF3dPointTo3DS()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - int l_ExportDF3dPointTo3DS2 (DFFace, pCallbackData);
 *
 * Local function used as a callback function for the CDF3dObject::ForEachFace()
 * method.  Outputs all the points in the face to the current position in the 
 * file stream contained in the callback data pointer.  Returns 0 on success.
 *
 * NOTE: Point Y-Z coordinates swapped, Z coordinate negated.  Points scaled 
 * down by DF3D_3DS_FACTOR.
 *
 * This version of the point export function should be paired with the
 * l_ExportDF3dFaceTo3DS2() function. These two functions use multiple points
 * where faces intersect (instead of a single point).  This method must be
 * used if materials are to be export to the 3DS file as mulitple UV texture
 * coordinates are required for each point on the face.
 *
 *=========================================================================*/
int l_ExportDF3dPointTo3DS2 (const df3dface_t& DFFace, void* pData) {
  //DEFINE_FUNCTION("l_ExportDF3dPointTo3DS2()");
  df3dexport3ds_t* pCallbackData = (df3dexport3ds_t *) pData;
  df3dpoint_t*     pPoint;
  CDF3dObject*     pDFObject = ((CDF3dObject * )(pCallbackData->pObject));
  int		   Result;
  int		   PointIndex;

	/* Create a unique point for each point on the face */
  for (PointIndex = 0; PointIndex < DFFace.NumPoints; PointIndex++) {
    pPoint = pDFObject->GetFacePoint(DFFace.FaceData[PointIndex].PointOffset);
    Result = l_ExportDF3dPointTo3DS(*pPoint, pData);
    if (Result < 0) return (-1);
   }
      
  return (0);
 }
/*===========================================================================
 *		End of Function l_ExportDF3dPointTo3DS2()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - 
 *	boolean ExportDF3dObjectTo3DS (File3DS, DF3dObject, pMeshMatrix, pName);
 *
 * Exports the given Daggerfall 3D object to a single trimesh chunk in the
 * 3DS file.  Returns FALSE on any error.  The optional mesh matrix
 * gives the 3x4 sized transformation matrix to use for transforming each
 * point.  If the given name string is not NULL, that name is used for the
 * 3DS object name.  Otherwise a default unique name is generated.
 *
 *=========================================================================*/
boolean ExportDF3dObjectTo3DS (C3dsFile& File3DS, const CDF3dObject& DF3dObject, 
			       const float* pMeshMatrix, const char* pName) {
  DEFINE_FUNCTION("ExportDF3dObjectTo3DS()");
  df3dexport3ds_t CallbackData;
  static long	  s_ObjectIndex = 1;
  char		  NameBuffer[64];
  boolean	  Result;
  int		  iResult;

  	/* Setup the callback data structure */
  CallbackData.pFile3DS      = &File3DS;
  CallbackData.pMeshMatrix   = pMeshMatrix;
  CallbackData.ObjectVersion = DF3dObject.GetVersionNum();
  CallbackData.pObject	     = &DF3dObject;
  CallbackData.PointIndex    = 0;
  
	/* Create the object name if required */
  if (pName == NULL) {
    snprintf (NameBuffer, 63, "df3d%03d", s_ObjectIndex);
    pName = NameBuffer;
    s_ObjectIndex++;
   }
  
	/* Start the object-trimesh chunks */
  Result  = File3DS.StartObjectChunk(pName);
  Result &= File3DS.StartTrimeshChunk();
  Result &= File3DS.StartPointChunk();
  if (!Result) return (FALSE);
  
	/* Output the point data */
  iResult = DF3dObject.ForEachFaceC(l_ExportDF3dPointTo3DS2, &CallbackData);
  if (iResult != 0) return (FALSE);
  Result = File3DS.EndPointChunk();
  if (!Result) return (FALSE);

	/* Output the texture vertices chunk */
  Result = File3DS.StartTexVertChunk();
  CallbackData.PointIndex = 0;
  iResult = DF3dObject.ForEachFaceC(l_ExportDFTexVertTo3DS, &CallbackData);
  Result &= File3DS.EndTexVertChunk(CallbackData.PointIndex);
  if (iResult < 0 || !Result) return (FALSE);
  CallbackData.PointIndex = 0;

	/* Start the mesh matrix and face chunks */
  Result  = File3DS.WriteMeshMatrix();
  Result &= File3DS.StartFaceChunk();
  if (!Result) return (FALSE);

	/* Output the face data */
  iResult = DF3dObject.ForEachFaceC(l_ExportDF3dFaceTo3DS2, &CallbackData);
  if (iResult != 0) return (FALSE);

	/* Output the face material grouping */
  iResult = ExportDFMaterialGroupsTo3DS(File3DS, DF3dObject);
  if (iResult != 0) return (FALSE);
    
  	/* End outtputing the 3DS object chunks */
  Result  = File3DS.EndFaceChunk();
  Result &= File3DS.EndTrimeshChunk();
  Result &= File3DS.EndObjectChunk();
  return (Result);
 }
/*===========================================================================
 *		End of Function ExportDF3dObjectTo3DS()
 *=========================================================================*/