#include <stdlib.h>
#include <stdio.h>
#include <direct.h>
#include <string.h>
#include <conio.h>
#include <ctype.h>
#include <math.h>

#include <pr.h>
#include <prgui.h>

#ifdef __3DFX__
#include <glide.h>
#include <pr3dfx.h>
#endif

#ifdef WIN32
#include <windows.h>
#endif

/*

       /=========\ /========\ ||        || /========  /=======\
       ||       || ||      || ||        || ||         ||      ||
       ||       || ||      || ||        || ||         ||      ||
       ||       || ||      || ||        || ||         ||      ||
       ||       || ||      || ||   ||   || ||         ||      //
       |=========/ ||      || ||   ||   || |=======   |=====<<
       ||          ||      || ||   ||   || ||         ||      \\
       ||          ||      || ||   ||   || ||         ||       ||
       ||          ||      || ||   ||   || ||         ||       ||
       ||          \========/ \==========/ \========= ||       ||

--------------------------------<===>----------------------------------

    /=======\   /========= |\     || |======\  /========= /=======\
    ||      ||  ||         ||\    || ||    \ \ ||         ||      ||
    ||      ||  ||         || \   || ||     || ||         ||      ||
    ||      ||  ||         ||  \  || ||     || ||         ||      ||
    ||      //  ||         ||   \ || ||     || ||         ||      //
    |======<<   |======    ||\   \|| ||     || |======    |======<<
    ||      \\  ||         || \   || ||     || ||         ||      \\
    ||       || ||         ||  \  || ||     || ||         ||       ||
    ||       || ||         ||   \ || ||    / / ||         ||       ||
    ||       || |========= ||    \|| |======/  |========= ||       ||

Lightwave Scene file to PRO Utility

Converts a Lightwave scene with animation to a PRO file

 Revision History:
 June 7, 1997: Created

 You may want to modify this program to load in .PRO files instead of
 LWO files.  This will let you apply textures to each part using
 texskin and then use the Lightwave scene file to combine them
 into one object.

*/

char outfile[80];               /* Name of the output file */
PR_OBJECT *object;
PR_DWORD choice;
PR_REAL scale = 1;
PR_DWORD usepro = 0;
PR_DWORD device;

PR_DWORD LWS_numseg, LWS_firstframe, LWS_lastframe;

void PreprocessLWS (char *filename)
{
FILE *lws;
char buf[128];
char buf2[128];
char *res;

  LWS_numseg = 0;

  lws = fopen (filename, "rt");
  if (lws == NULL)
    {
     printf ("Cannot open LWS file.\n");
     exit (1);
    }

  /* Get the first frame number */
  do {
    res = fgets (buf, 127, lws);
  } while ((res != NULL) && (strncmp(buf, "FirstFrame", 10) != 0));
  sscanf (buf, "%s %i", buf2, &LWS_firstframe);

  /* Get the last frame number */
  do {
    res = fgets (buf, 127, lws);
  } while ((res != NULL) && (strncmp (buf, "LastFrame", 9) != 0));
  sscanf (buf, "%s %i", buf2, &LWS_lastframe);


  /* Count the number of objects */
  do {
    res = fgets (buf, 127, lws);
    if (strncmp (buf, "LoadObject", 10) == 0)
      LWS_numseg++;
    if (strncmp (buf, "AddNullObject", 13) == 0)
      LWS_numseg++;
  } while (res != NULL);

  fclose (lws);
}


void RotateAxis (PR_POINT *axis, PR_MATRIX tempm)
{
  tvector[0] = axis->x;
  tvector[1] = axis->y;
  tvector[2] = axis->z;
  PR_Transform (tempm);
  axis->x = tvector[0];
  axis->y = tvector[1];
  axis->z = tvector[2];
}



void ProcessLWS (char *filename)
{
FILE *lws;
char buf[128], buf2[128], fname[80];
char *res;
PR_DWORD segnum, framenum;
PR_OBJECT *newobj;
PR_SEGMENT *seg, *parent, *tempseg;

PR_DWORD loadmode;
PR_DWORD numkeys, keynum, linflag;
PR_DWORD endbehavior;

PR_REAL rx, ry, rz, lrx, lry, lrz;
PR_REAL dx, dy, dz, ldx, ldy, ldz;
PR_POINT axis_x, axis_y, axis_z;
PR_QUATERNION qx, qy, qz, tempq;
PR_MATRIX tempm;
PR_DWORD len;

PR_CAMERA *cam = NULL;
PR_DWORD target_seg;

  loadmode = LOAD_IGNORE_TEXTURES;

  lws = fopen (filename, "rt");
  if (lws == NULL)
    {
     printf ("Cannot open LWS file.\n");
     exit (1);
    }

  segnum = 0;
  seg = object->segment_list;
  do {
    res = fgets (buf, 127, lws);
    if ((strncmp (buf, "LoadObject", 10) == 0) ||
        (strncmp (buf, "AddNullObject", 13) == 0))
      {
       sscanf (buf, "%s %s", buf2, fname);

       if (strncmp (buf, "LoadObject", 10) == 0)
         {
          if (!usepro)
            newobj = PR_LoadLWO (fname, scale, loadmode);

          else
            {
             len = strlen (fname);
             fname[len-3] = 'P';
             fname[len-2] = 'R';
             fname[len-1] = 'O';
             newobj = PR_LoadPRO (fname, LOAD_NORMAL);
            }

          if (newobj == NULL)
            {
             printf ("Cannot find %s.\n", fname);
             exit (1);
            }
          PR_InitializeFaceNormals (newobj);
          PR_InitializeVertexNormals (newobj);
         }
       else
         {
          newobj = PR_AllocObject ();
          newobj->num_segments = 1;
          newobj->segment_list = PR_AllocSegment ();
          newobj->segment_list->num_faces = 0;
          newobj->segment_list->num_vertices = 1;

          newobj->segment_list->vertex_list = malloc (sizeof (PR_VERTEX));
          newobj->segment_list->vertex_data = malloc (sizeof (PR_VERTEX_DATA));
          memset (newobj->segment_list->vertex_list, 0, 1 * sizeof (PR_VERTEX));
          memset (newobj->segment_list->vertex_data, 0, 1 * sizeof (PR_VERTEX_DATA));
          newobj->segment_list->vertex_list[0].vdata = &newobj->segment_list->vertex_data[0];

          newobj->segment_list->flags = 0;
         }

       memcpy (seg, newobj->segment_list, sizeof (PR_SEGMENT));
       seg->segment_number = segnum;
       seg->name = strdup (fname);


       /* Get the last frame number */
       do {
         res = fgets (buf, 127, lws);
        } while ((res != NULL) && (strncmp (buf, "ObjectMotion", 12) != 0));

       fgets (buf, 127, lws);  /* Skip over number of data elements */

       fgets (buf, 127, lws);  /* Number of keys */
       sscanf (buf, "%i", &numkeys);

       seg->pivot.x = 0;
       seg->pivot.y = 0;
       seg->pivot.z = 0;
       seg->level = -1;         /* No parent */

       seg->num_rot_keys = numkeys;
       seg->num_pos_keys = numkeys;
       seg->num_scl_keys = numkeys;
       seg->rot_keys = malloc (sizeof (PRKF_ROTKEY) * numkeys);
       seg->pos_keys = malloc (sizeof (PRKF_POSKEY) * numkeys);
       seg->scl_keys = malloc (sizeof (PRKF_SCLKEY) * numkeys);

       lrx = 0;  ldx = 0; /* Last rotation */
       lry = 0;  ldy = 0;
       lrz = 0;  ldz = 0;

       axis_x.x = 1;
       axis_x.y = 0;
       axis_x.z = 0;

       axis_y.x = 0;
       axis_y.y = 1;
       axis_y.z = 0;

       axis_z.x = 0;
       axis_z.y = 0;
       axis_z.z = 1;


       for (keynum = 0; keynum < numkeys; keynum++)
         {
          fgets (buf, 127, lws);  /* keyframe info */
          sscanf (buf, "%f %f %f %f %f %f %f %f %f",
               &seg->pos_keys[keynum].pos_info.x,
               &seg->pos_keys[keynum].pos_info.y,
               &seg->pos_keys[keynum].pos_info.z,
               &dy, &dx, &dz,
               &seg->scl_keys[keynum].scale_info.x,
               &seg->scl_keys[keynum].scale_info.y,
               &seg->scl_keys[keynum].scale_info.z);

          seg->pos_keys[keynum].pos_info.x *= scale;
          seg->pos_keys[keynum].pos_info.y *= scale;
          seg->pos_keys[keynum].pos_info.z *= scale;

          /* Now we must convert the rotation values
             into quaternions around the x,y,z axis */

          rx = RADIANS(-dx);
          ry = RADIANS(-dy);
          rz = RADIANS(-dz);

          PR_QuaternionFromVector (&qy, &axis_y, ry - lry);
          PR_MatrixRotate (tempm, 0, dy - ldy, 0);
          RotateAxis (&axis_x, tempm);
          RotateAxis (&axis_z, tempm);
          lry = ry; ldy = dy;

          PR_QuaternionFromVector (&qx, &axis_x, rx - lrx);
          PR_MatrixRotate (tempm, dx - ldx, 0, 0);
          RotateAxis (&axis_z, tempm);
          lrx = rx; ldx = dx;

          PR_QuaternionFromVector (&qz, &axis_z, rz - lrz);
          lrz = rz; ldz = dz;

          /* Multiply rotation about each to get the final quaternion */
          PR_QuaternionMultiply (&tempq, &qy, &qx);
          PR_QuaternionMultiply (&seg->rot_keys[keynum].rot_info, &tempq, &qz);

          fgets (buf, 127, lws);  /* curve info */
          sscanf (buf, "%i %i %f %f %f", &framenum, &linflag,
               &seg->pos_keys[keynum].key_header.spline_tension,
               &seg->pos_keys[keynum].key_header.spline_continuity,
               &seg->pos_keys[keynum].key_header.spline_bias);
          if (linflag)
            seg->pos_keys[keynum].key_header.spline_term_flags = KF_LINEAR_KEY;
          else
            seg->pos_keys[keynum].key_header.spline_term_flags = 0;

          memcpy (&seg->scl_keys[keynum].key_header,
                  &seg->pos_keys[keynum].key_header, sizeof (PRKF_KEYHDR));
          memcpy (&seg->rot_keys[keynum].key_header,
                  &seg->pos_keys[keynum].key_header, sizeof (PRKF_KEYHDR));

          seg->rot_keys[keynum].key_header.frame_number = framenum;
          seg->pos_keys[keynum].key_header.frame_number = framenum;
          seg->scl_keys[keynum].key_header.frame_number = framenum;
         }

       PR_MatrixTranslate (seg->localtoworld, seg->pos_keys[0].pos_info.x,
                 seg->pos_keys[0].pos_info.y, seg->pos_keys[0].pos_info.z);

       /* Get the last frame number */
       do {
         res = fgets (buf, 127, lws);

         if (strncmp (buf, "PivotPoint", 10) == 0)
           {
            sscanf (buf, "%s %f %f %f", buf2,
                &seg->pivot.x, &seg->pivot.y, &seg->pivot.z);
            seg->pivot.x *= scale;
            seg->pivot.y *= scale;
            seg->pivot.z *= scale;

            PR_MatrixTranslate (seg->localtoworld, seg->pos_keys[0].pos_info.x - seg->pivot.x,
                 seg->pos_keys[0].pos_info.y - seg->pivot.y, seg->pos_keys[0].pos_info.z - seg->pivot.z);
           }
         else if (strncmp (buf, "ParentObject", 12) == 0)
           {
            sscanf (buf, "%s %i", buf2, &seg->level);
            seg->level--;  /* 0 based */
           }
         else if (strncmp (buf, "EndBehavior", 11) == 0)
           {
            sscanf (buf, "%s %i", buf2, &endbehavior);
            if (endbehavior == 2)  /* looped animation */
              seg->flags |= FLAG_LOOP_ANIMATION;
           }

       } while ((res != NULL) && (strncmp (buf, "ShadowOptions", 13) != 0));

       seg++;
       segnum++;
      }


    if (strncmp (buf, "ShowCamera", 10) == 0)
      {
       cam = PR_AllocCamera ();
       PR_InitializeCamera (cam);

       do {
         res = fgets (buf, 127, lws);
        } while ((res != NULL) && (strncmp (buf, "CameraMotion", 12) != 0));

       fgets (buf, 127, lws);  /* Skip over number of data elements */

       fgets (buf, 127, lws);  /* Number of keys */
       sscanf (buf, "%i", &numkeys);

       cam->num_cam_keys = numkeys;
       cam->cam_keys = malloc (sizeof (PRKF_CAMKEY) * numkeys);

       for (keynum = 0; keynum < numkeys; keynum++)
         {
          fgets (buf, 127, lws);  /* keyframe info */
          sscanf (buf, "%f %f %f",
               &cam->cam_keys[keynum].src_info.x,
               &cam->cam_keys[keynum].src_info.y,
               &cam->cam_keys[keynum].src_info.z);

          cam->cam_keys[keynum].src_info.x *= scale;
          cam->cam_keys[keynum].src_info.y *= scale;
          cam->cam_keys[keynum].src_info.z *= scale;

          /* Set up some default data for the parts Lightwave doesn't
             supply */
          cam->cam_keys[keynum].dst_info.x = 0;
          cam->cam_keys[keynum].dst_info.y = 0;
          cam->cam_keys[keynum].dst_info.z = 0;
          cam->cam_keys[keynum].fov = (1.0f / 3.0f) * 3.1415f;
          cam->cam_keys[keynum].tilt = 0;

          fgets (buf, 127, lws);  /* curve info */
          sscanf (buf, "%i %i %f %f %f", &framenum, &linflag,
               &cam->cam_keys[keynum].key_header.spline_tension,
               &cam->cam_keys[keynum].key_header.spline_continuity,
               &cam->cam_keys[keynum].key_header.spline_bias);
          if (linflag)
            cam->cam_keys[keynum].key_header.spline_term_flags = KF_LINEAR_KEY;
          else
            cam->cam_keys[keynum].key_header.spline_term_flags = 0;

          cam->cam_keys[keynum].key_header.frame_number = framenum;
         }

       /* Get the last frame number */
       do {
         res = fgets (buf, 127, lws);

         if (strncmp (buf, "TargetObject", 12) == 0)
           {
            sscanf (buf, "%s %i", buf2,
                &target_seg);
            cam->target_segment = target_seg - 1;
            cam->flags |= CAMFLAG_TARGET_SEGMENT | CAMFLAG_AIM_TARGET;
           }
         else if (strncmp (buf, "EndBehavior", 11) == 0)
           {
            sscanf (buf, "%s %i", buf2, &endbehavior);
            if (endbehavior == 2)  /* looped animation */
              cam->flags |= CAMFLAG_LOOP_ANIMATION;
           }

       } while ((res != NULL) && (strncmp (buf, "TargetObject", 12) != 0));

       PR_AddCamera (cam);

      }


  } while (res != NULL);


  /* Make the links between child and parents */
  seg = object->segment_list;
  for (segnum = 0; segnum < LWS_numseg; segnum++)
    {
     if (seg->level != -1)
       {
        /* Add a child */
        tempseg = parent = &object->segment_list[seg->level];

        if (tempseg->children != NULL)
          {
           tempseg = tempseg->children;
           while (tempseg->next != NULL)
             tempseg = tempseg->next;
           tempseg->next = seg;
           seg->parent = parent;
           seg->flags |= FLAG_SUBSEG;
          }
        else
          {
           tempseg->children = seg;
           seg->parent = tempseg;
           seg->flags |= FLAG_SUBSEG;
          }
       }
     seg++;
    }

  fclose (lws);
}



void InitializeDevices (void)
{
#ifdef __MYST__
  if ((device == DEVICE_MYSTIQUE) || (device == DEVICE_ANY))
    device = PR_DetectMystique ();
#endif

#ifdef __3DFX__
  if ((device == DEVICE_3DFX) || (device == DEVICE_ANY))
    device = PR_Detect3Dfx ();
#endif

#if !defined (MSGLIDE) && !defined (WTGLIDE)
  #if defined (MSDD) || defined (WTDD)
    if ((device == DEVICE_D3D) || (device == DEVICE_ANY))
      device = PR_DetectD3D ();   /* Attempt to find the device */
  #endif

  if ((device == DEVICE_SVGA) || (device == DEVICE_ANY))
    device = PR_DetectSVGA ();   /* Attempt to find the device */

  if ((device == DEVICE_VGA) || (device == DEVICE_ANY))
    device = PR_DetectVGA ();   /* Attempt to find the device */
#endif

#ifdef __MYST__
  if (device == DEVICE_MYSTIQUE)
    {
     PR_InitializeMystique ();
     atexit (PR_ShutdownMystique);
    }
#endif

#ifdef __3DFX__
  if (device == DEVICE_3DFX)
    {
     PR_Initialize3Dfx ();
     atexit (PR_Shutdown3Dfx);
    }
#endif

#if !defined (MSGLIDE) && !defined (WTGLIDE)
  #if defined (MSDD) || defined (WTDD)
  if (device == DEVICE_D3D)
    {
     PR_InitializeD3D ();
     atexit (PR_ShutdownD3D);
    }
  #endif
  if (device == DEVICE_SVGA)
    {
     PR_InitializeSVGA ();
     atexit (PR_ShutdownSVGA);
    }
  else if (device == DEVICE_VGA)
    {
     PR_InitializeVGA ();
     atexit (PR_ShutdownVGA);
    }
#endif
}



void main (int argc, char *argv[])
{
PR_DWORD argnum;

  PRGUI_InitPath (argv[0]);
  PRGUI_SetUserPath ();

  if (argc < 2)
    {
     PR_FatalError (
           "LWS2PRO Utility    version "
           PR_VERSION_NUMBER
           "\nCopyright 1997 Egerter Software\n\n"
	   "Usage: \n"
       "LWS2PRO file.lws file.PRO [options]\n"
       "\n"
       "Options:\n"
       "-p            Uses PRO instead of LWO files\n"
       "-s scale      Changes scaling factor\n", "LWS2PRO");
    }

  printf ("LWS2PRO Utility    version ");
  printf (PR_VERSION_NUMBER);
  printf ("\nCopyright 1997 Egerter Software\n\n");

  argnum = 2;
  while (argnum < argc)
    {
     if (!strcmp (argv[argnum], "-s"))
       if (argc > argnum)
         {
          scale = atof (argv[argnum+1]);
          printf ("Scaling Factor: %f\n", scale);
          argnum++;
         }
     if (!strcmp (argv[argnum], "-p"))
         {
          usepro = 1;
         }
     argnum++;
    }


  if (PRGUI_FileExists (argv[2]))
    {
     #ifdef WIN32
     if (MessageBox(NULL, "The file already exists! Overwrite?\n",
                 "LWO2PRO", MB_OKCANCEL) != IDOK)
		exit (1);
	 #else
     printf ("%s already exists! Overwrite? (Y/N)\n",
            argv[2]);
     do {
       choice = toupper (getch ());
     } while (choice != 'N' && choice != 'Y');

     if (choice == 'N')
       exit (1);
	 #endif
    }


#if defined (MSGLIDE) || defined (WTGLIDE)
  device = DEVICE_3DFX;
#else
  device = DEVICE_SVGA;
#endif

  PR_Initialize (50);
  InitializeDevices ();

  PR_AllocMaterials (512);
  PR_AllocTextures (255);
  PR_AllocShadeTables (32);

  PreprocessLWS (argv[1]);


  /* Make the PRO object */
  object = PR_AllocObject ();
  if (object == NULL)
     PR_FatalError ("An error has occurred when allocating the combined object.\n", "LWS2PRO");

  /* Make a copy of the object's name */
  object->name = strdup (argv[1]);

  object->segment_list = (PR_SEGMENT *)calloc(LWS_numseg, sizeof (PR_SEGMENT));
  object->num_segments = LWS_numseg;
  /* Allocate space for the segment structures */

  ProcessLWS (argv[1]);
  
  PR_SavePRO (argv[2], object, SAVE_ALL_MATERIALS);

#ifndef WIN32
  wsetmode (3);
#endif
}


