]> git.pld-linux.org Git - packages/povray.git/commitdiff
UNSTABLE
authorMaciej Pijanka <agaran@pld-linux.org>
Sat, 30 Jun 2001 09:57:32 +0000 (09:57 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    povray-pvm.patch -> 1.1

povray-pvm.patch [new file with mode: 0644]

diff --git a/povray-pvm.patch b/povray-pvm.patch
new file mode 100644 (file)
index 0000000..3fd9d10
--- /dev/null
@@ -0,0 +1,3498 @@
+diff -Naur source.ori/optin.c source/optin.c
+--- source.ori/optin.c Sat Jun 30 01:49:21 2001
++++ source/optin.c     Sat Jun 30 10:01:28 2001
+@@ -30,7 +30,8 @@
+ *
+ *  Written by CEY 4/94 based on existing code and INI code from CDW.
+ *
+-*  ---
++*  Modified Oct. 96 by Deischi for PVM
++*  Modified Jun 2001 by Agaran for conditional PVM build
+ *
+ * Modification by Thomas Willhalm, March 1999, used with permission.
+ *
+@@ -56,7 +57,9 @@
+ #include "targa.h"
+ #include "userio.h"
+ #include "png_pov.h"
+-
++#ifdef USE_PVM
++#include "pvm.h"
++#endif
+ /*****************************************************************************
+@@ -187,7 +190,23 @@
+   { BITS_PER_COLOR_OP, "Bits_Per_Color" },
+   { BITS_PER_COLOUR_OP, "Bits_Per_Colour" },
++#ifdef USE_PVM
++ { INCLUDE_INI_OP, "Include_Ini" },
++
++ /* new options for PVM-Pov */
++ { PVM_TASKS_OP, "PVM_Tasks" },
++ { PVM_WIDTH_OP, "PVM_Width" },
++ { PVM_HEIGHT_OP, "PVM_Height" },
++ { PVM_SLAVE_OP, "PVM_Slave" },
++ { PVM_ARCH_OP, "PVM_Arch" },
++ { PVM_NICE_OP, "PVM_Nice" },
++ { PVM_WD_OP, "PVM_WD" },
++ { PVM_HOSTS_OP, "PVM_Hosts" },
++ { PVM_USE_OP, "PVM" }
++
++#else
+   { INCLUDE_INI_OP, "Include_Ini" }
++#endif
+ };
+ static char temp_string[3]="\0\0";
+@@ -229,7 +248,8 @@
+ *
+ * CHANGES
+ *
+-*   -
++*   Oct. 1996: Added options for PVMPOV [Deischi]
++*   jun 2001: conditional pvm
+ *
+ ******************************************************************************/
+@@ -560,6 +580,44 @@
+     case INCLUDE_INI_OP:
+       value[0] = '\0';
+       return value;
++#ifdef USE_PVM
++     /*
++      * PVM Options
++      */
++    case PVM_TASKS_OP:
++        sprintf(value, "%d", PvmTasks);
++        return value;
++    case PVM_WIDTH_OP:
++        sprintf(value, "%d", PvmChunkWidth);
++        return value;
++    case PVM_HEIGHT_OP:
++        sprintf(value, "%d", PvmChunkHeight);
++        return value;
++    case PVM_SLAVE_OP:
++        strncpy(value, PvmSlavename ? PvmSlavename : "", 128);
++        return value;
++    case PVM_ARCH_OP:
++        sprintf(value, "%s", PvmArch);
++        return value;
++    case PVM_NICE_OP:
++        sprintf(value, "%d", PvmNice);
++        return value;
++    case PVM_WD_OP:
++        strncpy(value, PvmWorkingDir, PATH_MAX);
++        return value;
++    case PVM_HOSTS_OP:
++        if(PvmHosts) {
++         int i;
++         for(i=0; i < PvmHostsN; i++) {
++             strncat(value, PvmHosts[i], 128);
++             strncat(value, ",", 128);
++         }
++        }
++        return value;
++    case PVM_USE_OP:
++        sprintf(value, "%s", (PvmTasks || PvmSlave) ? "On" : "Off");
++        return value;
++#endif
+     default:
+       Error("Unknown INI option in Write_INI.");
+@@ -602,6 +660,8 @@
+ *   Sep 1994 : Added options for union splitting, vista/light buffer. [DB]
+ *   Jan 1995 : Added options for histogram grid. [CJC]
+ *   Feb 1995 : Added options for console/file redirection and .INI writing [SCD]
++*   Oct 1996 : Added options for PVMPOV [Deischi]
++*   jun 2001 : added condititional build for pvm
+ *
+ ******************************************************************************/
+@@ -1024,13 +1084,67 @@
+       break;
+     /*  "N" option flag is used by networking (multi-processor) options.
+-
+-    case 'N':
+-    case 'n':
+-
+-      break;
+-
+      */
++#ifdef USE_PVM
++       /* 
++        * PVM-POV 
++        *
++        */
++   case 'N': /* switches used for PVMPOV */
++   case 'n':
++       switch( Option_String[1]) {
++       case '\0':     /* Start one slave/available PVM host */
++        process_variable(PVM_USE_OP, Add_Option ? "On" : "Off");
++        break;
++       case 'a': /* Select a particular architecture to run the slaves on */
++       case 'A':
++        process_variable(PVM_ARCH_OP, Option_String+2);
++        break;
++       case 'n': /* Sets the niceness (priority) level */
++       case 'N':
++        process_variable(PVM_NICE_OP, Option_String+2);
++        break;
++       case 't': /* Sets the number of tasks to run.  Default is 1 per host */
++       case 'T':
++        process_variable(PVM_TASKS_OP, Option_String+2);
++        break;
++       case 'h': /* The height of a single grid block, in pixels */
++       case 'H':
++        process_variable(PVM_HEIGHT_OP, Option_String+2);
++        break;
++       case 'w': /* The width of a single grid block, in pixels */
++       case 'W':
++        process_variable(PVM_WIDTH_OP, Option_String+2);
++        break;          
++       case 's': /* specify the name of the slave */
++       case 'S':
++        process_variable(PVM_SLAVE_OP, Option_String+2);
++        break;
++       case 'd': /* specify working directory for the slaves */
++       case 'D':
++         process_variable(PVM_WD_OP, Option_String+2);
++         break;
++        /* Internal sub-option "-nI" indicates a PVM slave task.
++         * This will be appended to current argv string, so it will override
++         * the previous -nxx options. */
++       case 'i':
++       case 'I':
++        PvmTasks = 1;
++        PvmSlave = 1;
++        
++        if (chdir(&Option_String[2]) < 0) {
++            fprintf(stderr, "Slave cannot cd to %s.\n", &Option_String[2]);
++            exit(1);
++        }
++        break;
++        
++       default:
++        fprintf(stderr, "Illegal +N%c switch setting\n", Option_String[1]);
++        break;
++       } /* switch */
++       break;
++#endif
++     
+     case 'O':
+     case 'o':
+@@ -1262,7 +1376,8 @@
+ *
+ * CHANGES
+ *
+-*   -
++*   Oct 1996 : Added options for PVMPOV [Deischi]
++*   jun 2001 : conditional build for pvm
+ *
+ ******************************************************************************/
+@@ -1445,21 +1560,36 @@
+       return;
+     case END_COLUMN_OP:
++#ifdef USE_PVM
++       /* tw */
++      if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF) {
++#else
+       { /* tw */
+       if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF)
++#endif
+         if(floatval > 0.0 && floatval <= 1.0)
+         {
+           opts.Last_Column = -1;
+           opts.Last_Column_Percent = floatval;
+         }
++#ifdef USE_PVM
++        else {
++        opts.Last_Column = (int) floatval; }
++#else
+         else
+           opts.Last_Column = (int) floatval;
++#endif
+       } /* tw */
+       return;
+     case END_ROW_OP:
++#ifdef USE_PVM
++       /* tw */
++      if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF) {
++#else
+       { /* tw */ 
+       if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF)
++#endif
+         if(floatval > 0.0 && floatval <= 1.0)
+         {
+           opts.Last_Line = -1;
+@@ -1831,6 +1961,86 @@
+         Error ("Could not open Include_Ini='%s'.\n", value);
+       }
+       return;
++
++#ifdef USE_PVM
++      /* 
++       * Options for PVMPOV
++       */
++   case PVM_TASKS_OP:
++       if (sscanf(value, "%d", &PvmTasks) != 1 || PvmTasks < 1) { 
++        PvmTasks = 9999;   /* Try to start one slave/available PVM host */
++       } 
++       return;
++ 
++   case PVM_WIDTH_OP:
++       if (sscanf(value, "%d", &PvmChunkWidth) != 1 || PvmChunkWidth < 4)
++        PvmChunkWidth = PVM_DEFAULT_GRID_WIDTH;
++       return;
++ 
++   case PVM_HEIGHT_OP:
++       if (sscanf(value, "%d", &PvmChunkHeight) != 1 || PvmChunkHeight < 4)
++        PvmChunkHeight = PVM_DEFAULT_GRID_HEIGHT;
++       return;
++        
++   case PVM_SLAVE_OP:
++       PvmSlavename = POV_MALLOC( sizeof(char)*(strlen(value)+1), 
++                               "PVM Slavename");
++       strcpy(PvmSlavename, value);
++       return;
++        
++   case PVM_ARCH_OP:
++       strncpy(PvmArch,value,19);
++       PvmArch[19] = '\0';
++        
++       if (PvmTasks < 1)
++        PvmTasks = 9999;   /* Try to start one slave/available PVM host */
++       return;
++ 
++   case PVM_NICE_OP:
++       if (sscanf(value, "%d", &PvmNice) != 1 || PvmNice < 0) {
++        PvmNice = PVM_DEFAULT_NICE;
++       }
++       return;
++
++   case PVM_WD_OP:
++       strncpy(PvmWorkingDir, value, PATH_MAX);
++       return;
++
++   case PVM_HOSTS_OP: {
++       char * c = value;
++       char * c2;
++       int n;
++
++       PvmHostsN = 1;                 /* count number of names */
++       while((c = strchr(c, ',')) != NULL) {
++         c ++;
++         PvmHostsN ++;
++       }
++       PvmHosts = (char**)POV_MALLOC( sizeof(char*) * PvmHostsN, "PvmHosts");
++
++       c = value;
++       c2 = strchr(c,',');
++       for(i=0; i < PvmHostsN; i++) {
++         n = (c2) ? c2 - c : strlen(c);
++         PvmHosts[i] = (char*)POV_MALLOC( sizeof(char) * (n + 1), "PvmHosts[i]");
++         strncpy(PvmHosts[i], c, n);
++         PvmHosts[i][n] = '\0';
++
++         if(c2) c = c2 + 1;
++         c2 = strchr(c, ',');
++       }
++
++       PvmTasks = PvmHostsN;
++
++       return;
++
++   }
++   case PVM_USE_OP:
++       PvmTasks = istrue(value) ? 9999 : 0;
++       return;
++
++#endif 
++
+     default:
+       Warning(0.0,"Unimplemented INI '%s'.\n",Option_Variable[variable].Token_Name);
+diff -Naur source.ori/optin.h source/optin.h
+--- source.ori/optin.h Sat Jun 30 01:49:21 2001
++++ source/optin.h     Sat Jun 30 03:43:53 2001
+@@ -19,6 +19,10 @@
+ * DKBTrace was originally written by David K. Buck.
+ * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+ *
++*
++* Oct 1996 : Added options for PVMPOV [Deischi]
++* Jun 2001 : Added conditional build for PVM Support [Agaran]
++*
+ *****************************************************************************/
+@@ -131,6 +135,19 @@
+   BITS_PER_COLOR_OP,
+   BITS_PER_COLOUR_OP,
+   INCLUDE_INI_OP,
++
++#ifdef USE_PVM
++  /* new options for PVMPOV */
++  PVM_TASKS_OP,
++  PVM_WIDTH_OP,
++  PVM_HEIGHT_OP,
++  PVM_SLAVE_OP,
++  PVM_ARCH_OP,
++  PVM_NICE_OP,
++  PVM_WD_OP,
++  PVM_HOSTS_OP,
++  PVM_USE_OP,
++#endif
+   MAX_OPTION
+ } INI_OP;
+diff -Naur source.ori/optout.c source/optout.c
+--- source.ori/optout.c        Sat Jun 30 01:49:21 2001
++++ source/optout.c    Sat Jun 30 09:25:02 2001
+@@ -19,6 +19,9 @@
+ * DKBTrace was originally written by David K. Buck.
+ * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+ *
++* Oct 1996 : Added options for PVMPOV [Deischi]
++* jun 2001 : added conditional build for pvm
++*
+ *****************************************************************************/
+ #include <ctype.h>
+@@ -65,8 +68,9 @@
+ #include "povray.h"
+ #include "optin.h"
+ #include "optout.h"
+-
+-
++#ifdef USE_PVM
++#include "pvm.h"
++#endif
+ /*****************************************************************************
+ * Local preprocessor defines
+@@ -611,6 +615,9 @@
+ *   Dec 1994 : Changed to show options depending on parameter n. [DB]
+ *
+ *   Feb 1995 : Changed to terminate only if f != 0. [DB]
++*   
++*   Oct 1996 : Added options from PVMPOV [Deischi]
++*   Jun 2001 : conditional build for pvm
+ *
+ ******************************************************************************/
+@@ -640,7 +647,12 @@
+       Banner("    4     Output Options - file related\n");
+       Banner("    5     Tracing Options\n");
+       Banner("    6     Animation Options\n");
++#ifdef USE_PVM
++      Banner("    7     PVM Options\n");
++      Banner("    8     Redirecting Options\n");
++#else
+       Banner("    7     Redirecting Options\n");
++#endif
+       break;
+@@ -767,6 +779,40 @@
+       break;
++#ifdef USE_PVM
++    /* PVM options. */
++
++    case 7:
++
++      Banner("\n");
++      Banner("PVM options\n");
++      Banner("\n");
++      Banner("  N         = Turn on PVM\n");
++      Banner("  NTnnn     = Spawn number of tasks\n");
++      Banner("  NAs       = Spawn only on architecture\n");
++      Banner("  NWnnn     = Width of each chunk\n");
++      Banner("  NHnnn     = Height of each chunk\n");
++      Banner("  NNnnn     = Niceness of slaves\n");
++      Banner("  NSs       = Name of slaves (executable)\n");
++      Banner("  NDnnn     = Name of the working directory\n");
++      Banner("  pvm_hosts = n,n,..  = List of hostnames to use\n");
++      Banner("\n");
++
++    /* Redirecting options. */
++
++      Banner("\n");
++      Banner("Redirecting options\n");
++      Banner("\n");
++      Banner("  GI<name>= write all .INI parameters to file name\n");
++      Banner("  Gx<name>= write stream x to console and/or file name\n");
++      Banner("            GA - All streams (except status)\n");
++      Banner("            GD - Debug stream\n");
++      Banner("            GF - Fatal stream\n");
++      Banner("            GR - Render stream\n");
++      Banner("            GS - Statistics stream\n");
++      Banner("            GW - Warning stream\n");
++#else 
++
+     /* Redirecting options. */
+     case 7:
+@@ -782,6 +828,7 @@
+       Banner("            GR - Render stream\n");
+       Banner("            GS - Statistics stream\n");
+       Banner("            GW - Warning stream\n");
++#endif
+       break;
+@@ -867,7 +914,8 @@
+ *
+ * CHANGES
+ *
+-*   -
++*   Oct 1996 : Added options from PVMPOV [Deischi]
++*   Jun 2001 : Added conditional build for PVM Support [Agaran]
+ *
+ ******************************************************************************/
+@@ -1089,6 +1137,26 @@
+   }
+   Render_Info("\n");
++#if USE_PVM
++  if(PvmTasks) {
++      int i;
++      Render_Info("PVM Options\n");
++      Render_Info("  Block Width....%8d", PvmChunkWidth);
++      Render_Info("  Block Height...%8d\n", PvmChunkHeight);
++      Render_Info("  PVM Tasks......%8d\n", PvmTasks);
++      Render_Info("  PVM Nice.......%8d\n", PvmNice);
++      Render_Info("  PVM Arch....... %s\n", PvmArch);
++      Render_Info("  PVM Slave...... %s\n", PvmSlavename ? PvmSlavename : "");
++      Render_Info("  PVM WorkingDir. %s\n", PvmWorkingDir);
++      if(PvmHosts) {
++        Render_Info("  Pvm Hosts......");
++        for(i=0; i < PvmHostsN; i++) {
++            Render_Info(" %s", PvmHosts[i]);
++        }
++        Render_Info("\n");
++      }
++  }
++#endif
+   /* Print redirecting options. */
+diff -Naur source.ori/optout.h source/optout.h
+--- source.ori/optout.h        Sat Jun 30 01:49:21 2001
++++ source/optout.h    Sat Jun 30 02:17:45 2001
+@@ -19,6 +19,10 @@
+ * DKBTrace was originally written by David K. Buck.
+ * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+ *
++* Oct 1996 : Added options for PVMPOV [Deischi]
++* May 1999 : Changed code to work with POVRAY 3.1e [Flierl]
++* Jun 2001 : conditional build for PVM [agaran]
++*
+ *****************************************************************************/
+@@ -36,12 +40,19 @@
+ #define POV_RAY_VERSION "3.1g"
+ #define DISTRIBUTION_MESSAGE_1 "This is an unofficial version compiled by:"
++#ifdef USE_PVM
++#define DISTRIBUTION_MESSAGE_2 "Jakob Flierl <flierl@luga.de> - PVMPOV Version 3.1e.2"
++#else
+ #define DISTRIBUTION_MESSAGE_2 "FILL IN NAME HERE........................."
++#endif
+ #define DISTRIBUTION_MESSAGE_3 "The POV-Ray Team(tm) is not responsible for supporting this version."
+ /* Number of help pages (numbered 0 to MAX_HELP_PAGE). */
+-
++#ifdef USE_PVM
++#define MAX_HELP_PAGE 8
++#else
+ #define MAX_HELP_PAGE 7
++#endif
+diff -Naur source.ori/povray.c source/povray.c
+--- source.ori/povray.c        Sat Jun 30 01:49:21 2001
++++ source/povray.c    Sat Jun 30 09:55:32 2001
+@@ -20,6 +20,10 @@
+ * DKBTrace was originally written by David K. Buck.
+ * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+ *
++* Oct 1996 : Added options for PVMPOV [Deischi]
++* May 1999 : Changed code to work with POVRAY 3.1e [Flierl]
++* Jun 2001 : Added conditional build for PVM [Agaran]
++*
+ *****************************************************************************/
+ #include <ctype.h>
+@@ -69,7 +73,9 @@
+ #include "userio.h"     /*Error,Warning,Init_Text_Streams*/
+ #include "lbuffer.h"
+ #include "vbuffer.h"
+-
++#ifdef USE_PVM
++#include "pvm.h"
++#endif
+ /*****************************************************************************
+ * Local preprocessor defines
+@@ -157,9 +163,15 @@
+ static void fix_up_rendering_window (void);
+ static void fix_up_animation_values (void);
+ static void fix_up_scene_name (void);
++#ifdef USE_PVM
++void set_output_file_handle (void);
++void setup_output_file_name (void);
++void open_output_file (void);
++#else
+ static void set_output_file_handle (void);
+ static void setup_output_file_name (void);
+ static void open_output_file (void);
++#endif
+ static void FrameRender (void);
+ static void init_statistics (COUNTER *);
+ static void sum_statistics (COUNTER *, COUNTER *);
+@@ -192,7 +204,8 @@
+ *
+ * CHANGES
+ *
+-*   -
++*   Oct 1996 : Added PVM Support [Deischi]
++*   Jun 2001 : Added conditional build for PVM Support [Agaran]
+ *
+ ******************************************************************************/
+@@ -262,17 +275,31 @@
+   }
+ #endif
++#ifdef USE_PVM
++  /* If slave, turn off certain optiongs */
++  if ( PvmSlave)
++      opts.Options &= ~(DISPLAY | VERBOSE);
++#endif
++
+   /* Strip path and extension off input name to create scene name */
+   fix_up_scene_name ();
+   
+   /* Redirect text streams [SCD 2/95] */
+   Open_Text_Streams();
++#ifdef USE_PVM
++  if ( !PvmSlave) {
++      /* Write .INI file [SCD 2/95] */
++      Write_INI_File();
++
++      ALT_WRITE_INI_FILE
++  }
++#else
+   /* Write .INI file [SCD 2/95] */
+   Write_INI_File();
+   
+   ALT_WRITE_INI_FILE
+-
++#endif
+   /* Make sure clock is okay, validate animation parameters */
+   fix_up_animation_values();
+@@ -282,9 +309,18 @@
+   /* Set output file handle for options screen. */
+   set_output_file_handle();
++#ifdef USE_PVM
++  /* Initialize PVM master */
++  if ( !PvmSlave && PvmTasks)
++      Pvm_Master_Init(argc, argv);
++
++  /* Print options used. */
++  if ( !PvmSlave)
++      Print_Options();
++#else
+   /* Print options used. */
+   Print_Options();
+-  
++#endif
+   /* BEGIN SECTION */
+   /* VARIOUS INITIALIZATION THAT ONLY NEEDS TO BE DONE 1/EXECUTION */
+@@ -306,6 +342,32 @@
+   /* END SECTION */
++#ifdef USE_PVM
++  /* PVM Slave */
++  if (PvmSlave ) {
++      if( Pvm_Slave_Init() < 0)
++        return;
++
++      Pvm_Slave_Control();            /* get the first block to do */
++      while(PvmNextFrame <= opts.FrameSeq.FinalFrame) {
++        /* set frame number */
++        opts.FrameSeq.FrameNumber = PvmNextFrame;
++        /* calculate clock value */
++        opts.FrameSeq.Clock_Value = opts.FrameSeq.InitialClock +
++            ((DBL)(opts.FrameSeq.FrameNumber - opts.FrameSeq.InitialFrame)) *
++            Clock_Delta;
++
++        FrameRender();
++      }
++
++      Pvm_Slave_Exit();
++
++      Terminate_POV(0);
++
++      MAIN_RETURN_STATEMENT
++  }
++#endif
++
+   /* Execute the first shell-out command */
+   Pre_Scene_Result=(POV_SHELLOUT_CAST)POV_SHELLOUT(PRE_SCENE_SHL);
+@@ -314,6 +376,26 @@
+   if (Pre_Scene_Result != ALL_SKIP_RET)
+   {
+      if (Pre_Scene_Result != SKIP_ONCE_RET)
++#ifdef USE_PVM
++       if(!PvmSlave && PvmTasks) {
++           START_TIME
++           Pvm_Master_Control();     /* distribute work,
++                                        and collect results,
++                                        call shellout commands */
++           STOP_TIME
++           trender = TIME_ELAPSED
++
++           PRINT_STATS(totalstats);
++       } else {
++         for (opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame,
++              opts.FrameSeq.Clock_Value = opts.FrameSeq.InitialClock;
++
++              opts.FrameSeq.FrameNumber <= opts.FrameSeq.FinalFrame;
++
++              opts.FrameSeq.FrameNumber++,
++              opts.FrameSeq.Clock_Value += Clock_Delta)
++       {
++#else
+      {
+        for (opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame,
+             opts.FrameSeq.Clock_Value = opts.FrameSeq.InitialClock;
+@@ -323,6 +405,7 @@
+             opts.FrameSeq.FrameNumber++,
+             opts.FrameSeq.Clock_Value += Clock_Delta)
+        {
++#endif
+          setup_output_file_name();
+          /* Execute a shell-out command before tracing */
+@@ -348,7 +431,6 @@
+            }
+          }
+        }
+-
+        /* Print total stats ... */
+  
+        if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME)
+@@ -399,6 +481,8 @@
+ * CHANGES
+ *
+ *   Feb 1996: Make sure we are displaying when doing a mosaic preview [AED]
++*   Oct 1996: Added PVM support [Deischi]
++*   Jun 2001: Added conditional build for PVM Support [agaran]
+ *
+ ******************************************************************************/
+@@ -483,6 +567,30 @@
+   /* Save variable values. */
+   variable_store(STORE);
++#ifdef USE_PVM
++  if( !PvmSlave) {
++
++  /* Open output file and if we are continuing an interrupted trace,
++   * read in the previous file settings and any data there.  This has to
++   * be done before any image-size related allocations, since the settings
++   * in a resumed file take precedence over that specified by the user. [AED]
++   */
++    open_output_file();
++
++    /* Start the display. */
++    if (opts.Options & DISPLAY)
++    {
++      Status_Info ("\nDisplaying...");
++
++      POV_DISPLAY_INIT(Frame.Screen_Width, Frame.Screen_Height);
++
++      Display_Started = TRUE;
++
++      /* Display vista tree. */
++      Draw_Vista_Buffer();
++    }
++  } /* !PvmTasks */
++#else
+   /* Open output file and if we are continuing an interrupted trace,
+    * read in the previous file settings and any data there.  This has to
+    * be done before any image-size related allocations, since the settings
+@@ -502,15 +610,45 @@
+     /* Display vista tree. */
+     Draw_Vista_Buffer();
+   }
+-
++#endif
+   /* Get things ready for ray tracing (misc init, mem alloc) */
+   Initialize_Renderer();
++#ifdef USE_PVM
++  /* If a PVM slave, re-assign Write_Line to PVM */
++  if (PvmSlave) {
++      Output_File_Handle->Write_Line_p = Pvm_Write_Line;
++  }
++#endif
+   /* This had to be taken out of open_output_file() because we don't have
+    * the final image size until the output file has been opened, so we can't
+    * initialize the display until we know this, which in turn means we can't
+    * read the rendered part before the display is initialized. [AED]
+    */
++#ifdef USE_PVM
++  if (!PvmSlave) {
++    if ((opts.Options & DISKWRITE) && (opts.Options & CONTINUE_TRACE))
++    {
++      Read_Rendered_Part(Actual_Output_Name);
++
++      if (opts.Last_Line > Frame.Screen_Height)
++        opts.Last_Line = Frame.Screen_Height;
++
++      if (opts.Last_Column > Frame.Screen_Width)
++        opts.Last_Column = Frame.Screen_Width;
++    }
++  } /* !PvmSlave */
++  if(PvmSlave || !PvmTasks) {
++    /* Get parsing time. */
++    STOP_TIME
++    tparse = TIME_ELAPSED
++
++    /* Get total parsing time. */
++    tparse_total += tparse;
++
++  } /* PvmSlave || !PvmTasks */
++
++#else
+   if ((opts.Options & DISKWRITE) && (opts.Options & CONTINUE_TRACE))
+   {
+     Read_Rendered_Part(Actual_Output_Name);
+@@ -529,7 +667,7 @@
+   /* Get total parsing time. */
+   tparse_total += tparse;
+-
++#endif
+   /* Store start time for trace. */
+   START_TIME
+@@ -547,7 +685,65 @@
+   POV_PRE_RENDER
+   Status_Info ("\nRendering...\r");
++#ifdef USE_PVM
++  do {
++    /* Macro for setting up any special FP options */
++    CONFIG_MATH
++
++    /* Ok, go for it - trace the picture. */
++
++    /* If radiosity preview has been done, we are continuing a trace, so it
++     * is important NOT to do the preview, even if the user requests it, as it
++     * will cause discontinuities in radiosity shading by (probably) calculating
++     * a few more radiosity values.
++     */
++    if ( !opts.Radiosity_Preview_Done )
++    {
++      if ( opts.Options & RADIOSITY )
++      {
++        /* Note that radiosity REQUIRES a mosaic preview prior to main scan */
++
++        Start_Tracing_Mosaic_Smooth(opts.PreviewGridSize_Start, opts.PreviewGridSize_End);
++      }
++      else
++      {
++        if (opts.Options & PREVIEW && opts.Options & DISPLAY)
++        {
++          Start_Tracing_Mosaic_Preview(opts.PreviewGridSize_Start, opts.PreviewGridSize_End);
++        }
++      }
++    }
++
++    switch (opts.Tracing_Method)
++    {
++      case 2 :
++
++        Start_Adaptive_Tracing();
++
++        break;
++
++      case 1 :
++      default:
++
++        Start_Non_Adaptive_Tracing();
++    }
++  } while(PvmSlave ? Pvm_Slave_Control() : 0);        /* ask for more work */
++
++  /* We're done. */
++
++  /* Record time so well spent before file close so it can be in comments  */
++  STOP_TIME
++  trender = TIME_ELAPSED
++
++  if(!PvmSlave) {
++      /* Close out our file */
++      if (Output_File_Handle)
++      {
++        Close_File(Output_File_Handle);
++      }
++  }
++#else
+   /* Macro for setting up any special FP options */
+   CONFIG_MATH
+@@ -600,7 +796,7 @@
+   {
+      Close_File(Output_File_Handle);
+   }
+-
++#endif
+   Stage = STAGE_SHUTDOWN;
+   POV_PRE_SHUTDOWN
+@@ -626,6 +822,27 @@
+   /* Get total render time. */
+   trender_total += trender;
++#ifdef USE_PVM
++  if(!PvmSlave) {
++    POV_DISPLAY_FINISHED
++
++    if ((opts.Options & DISPLAY) && Display_Started)
++    {
++      POV_DISPLAY_CLOSE
++
++      Display_Started = FALSE;
++    }
++
++    if (opts.histogram_on)
++      write_histogram (opts.Histogram_File_Name) ;
++  }
++  Status_Info("\nDone Tracing");
++
++  /* Print stats ... */
++  if(!PvmSlave)
++    PRINT_STATS(stats);
++
++#else
+   POV_DISPLAY_FINISHED
+   if ((opts.Options & DISPLAY) && Display_Started)
+@@ -642,7 +859,7 @@
+   /* Print stats ... */
+   PRINT_STATS(stats);
+-
++#endif
+   if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME)
+   {
+     /* Add them up */
+@@ -1030,8 +1247,11 @@
+ *          NULL function pointer.
+ *
+ ******************************************************************************/
+-
++#ifdef USE_PVM
++void set_output_file_handle()
++#else
+ static void set_output_file_handle()
++#endif
+ {
+   char *def_ext = NULL;
+   char temp[FILE_NAME_LENGTH];
+@@ -1110,7 +1330,11 @@
+ *                  function appends a path separator on output.
+ *
+ ******************************************************************************/
++#ifdef USE_PVM
++void setup_output_file_name()
++#else
+ static void setup_output_file_name()
++#endif
+ {
+   char number_string[10];
+   char separator_string[2] = {FILENAME_SEPARATOR, 0} ;
+@@ -1219,8 +1443,11 @@
+ *   -
+ *
+ ******************************************************************************/
+-
++#ifdef USE_PVM
++void open_output_file()
++#else
+ static void open_output_file()
++#endif
+ {
+   int Buffer_Size;
+   
+@@ -1378,12 +1605,18 @@
+   opts.Radiosity_Nearest_Count = 6;
+   opts.Radiosity_Recursion_Limit = 1;
+   opts.Radiosity_Quality = 6;     /* Q-flag value for light gathering */
++#ifdef USE_PVM
++  opts.Radiosity_File_ReadOnContinue = PvmSlave ? 0 : 1;
++  opts.Radiosity_File_SaveWhileRendering = PvmSlave ? 0 : 1; /* Don't save radiostiry cache with PVM */
++  opts.Radiosity_File_AlwaysReadAtStart = 0;
++  opts.Radiosity_File_KeepOnAbort = PvmSlave ? 0 : 1;
++#else
+   opts.Radiosity_File_ReadOnContinue = 1;
+   opts.Radiosity_File_SaveWhileRendering = 1;
+   opts.Radiosity_File_AlwaysReadAtStart = 0;
+   opts.Radiosity_File_KeepOnAbort = 1;
++#endif
+   opts.Radiosity_File_KeepAlways = 0;
+-
+   init_statistics(stats);
+   init_statistics(totalstats);
+diff -Naur source.ori/pvm/PVMPOV.Changelog source/pvm/PVMPOV.Changelog
+--- source.ori/pvm/PVMPOV.Changelog    Thu Jan  1 01:00:00 1970
++++ source/pvm/PVMPOV.Changelog        Sun Sep 12 00:25:35 1999
+@@ -0,0 +1,17 @@
++ * 3.1e.2 [Jakob Flierl]
++  - fixed problem with the statistics collection in line 1331 of "pvm.c"
++  - minor changes in "sources/pvm/Makefile.aimk"
++  - updated documentation
++  - added "PVMPOV.Changelog" and "PVMPOV.benchmark"
++ * 3.1e.1 [Jakob Flierl]
++  - applied the patch to POV-Ray version 3.1e
++  - fixed at least partially a memory bug in "render.c". Thanks to Michael
++    Eilers for solving this problem at least partially.
++ * 3.0x [Harald Deischinger]
++  - applied the patch to POV-Ray 3.01 and 3.01
++  - several command line enhancements.
++ * 2.2 to 2.9 [Andreas Dilger]
++  - changes in optin.h, optin.c, optout.h, optout.c,
++               povray.c, render.h, render.c
++  - added pvm/Makefile.aimk, pvm/pvm.h, pvm/pvm.c
++ * Initial Version [Brad Kline, Cray Inc.]
+diff -Naur source.ori/pvm/PVMPOV.benchmark source/pvm/PVMPOV.benchmark
+--- source.ori/pvm/PVMPOV.benchmark    Thu Jan  1 01:00:00 1970
++++ source/pvm/PVMPOV.benchmark        Sun Sep 12 00:25:35 1999
+@@ -0,0 +1 @@
++Surf to http://www.haveland.com/povbench/ to look at some render statistics.
+diff -Naur source.ori/pvm/PVMPOV.example source/pvm/PVMPOV.example
+--- source.ori/pvm/PVMPOV.example      Thu Jan  1 01:00:00 1970
++++ source/pvm/PVMPOV.example  Sun Sep 12 00:25:35 1999
+@@ -0,0 +1,283 @@
++This file describes the command line options of PVMPOV, as well as the general
++theory behind how it operates.
++
++Theory of Operation
++-------------------
++Using the PVM model, there is one master and many slave tasks. The master has 
++the responsibility of issuing the work sections to the slaves, and receiving 
++portions of the rendered sections back.  The master does not render anything
++by itself.  The PVM component is only active if the user gives the "+N" option
++to POV.  Otherwise, PVMPOV behaves the same as regular POV-Ray and runs a
++single task only on the local machine.
++
++Valid POV-Ray command-line options relating to PVM are:
++
++    pvm=on
++    +N      This is the default for starting PVMPOV.  One slave will be
++          started on each available host, regardless of architecture, and
++          the blocks will be 32x32 pixels in size.  The slaves will be
++          started with a nice value of 5, which means they will run at a
++          lower priority than other user jobs. 
++    pvm=off
++    -N      Turns of PVM support. PVMPOV now runs exactly as normal POV-Ray. 
++            You should do this when there are still errors in your pov-file. 
++            PVMPOV will not display error message otherwise.
++
++    pvm_tasks=xx
++    +NTxx   Start xx tasks on the available PVM hosts.  This is usually only
++            useful if debugging on a single machine, or for starting more
++          than 1 task on multi-processor hosts.  If, for example, you have
++          10 machines with 4 CPUs each, you could specify +nt40 to start 4
++          processes on each host (and the OS will hopefully run 1 on each
++          CPU).  
++
++            Note that PVM is stupid in the way it starts tasks, so if,
++          in the previous example, one of the hosts has only one CPU, it will
++          still have 4 slaves started on it. You can use the "pvm_hosts"
++            option (see below) to control on which machines the tasks are 
++            started.
++
++            Starting multiple tasks on a single processor will always be less 
++            efficient than a single task because of context switching and extra 
++            message passing.
++
++    pvm_arch=arch
++    +NAarch Start the tasks only on the PVM architecture "arch". If +NT is not
++          given, one task will be started on each of the hosts of the given
++          architecture.
++
++    pvm_nice=xx
++    +NNxx   Run the slaves at a niceness factor of xx.  The default niceness
++          is 5.  In general, changing the niceness value will not affect
++          performance very much, but may get others upset with you.  The
++          nicest setting for pvmpov is 20, while the least nice setting is
++          0.  Note that these values are always used even on systems that
++          use nice values from 20-40.  See the installation document and
++          the man page for nice for more information.
++
++    pvm_width=xx
++    +NWxx   Change the width of the blocks to xx pixels.  The default width
++          is 32 pixels.
++
++    pvm_height=xx
++    +NHxx   Change the height of the blocks to xx pixels.  The default height
++          is 32 pixels.
++
++            It is important to note that by varying the size of the grid
++          sections, you can affect the performance of the rendering.  If you
++          have particular renderings that are very complex in a small portion
++          of the display, then a finer grain may help.  In this way, more of
++          the tasks are able to migrate towards the grid sections that are
++          more complex.  Conversely, if you have a shorter render or a 
++          slower network, it may be advantageous to have larger blocks to
++          reduce network overhead, as well as ensure the slaves are not idle
++          waiting for blocks to render.
++
++          You must also consider overhead if using anti-aliasing.  Anti-
++          aliasing requires the line segment above and below the grid section
++          to be traced so that super-sampling may occur.  If the height of the
++          grid is reduced in size, and anti-aliasing is turned on, your
++          percentage of overhead goes up.  For example, setting a height of
++          four (-NH4) using anti-aliasing would have more than 25% overhead.
++          If the image size is not an integer multiple of the grid size, the
++          edge blocks are smaller (ie extra pixels aren't rendered), so it
++          is not necessary to evenly divide the image into blocks.
++
++     pvm_slave=slave
++     +NSslave  Uses slave as filename for the slave tasks. If you do not 
++            specify this option PVMPOV will use the current executable name
++            also for the slaves. 
++
++            Using this option you can for example run the X11 version
++            (x-pvmpov) as master (to display the results) and a version
++            without any display support (pvmpov) as slaves.
++
++     pvm_wd=dir
++     +NDdir Set the working directory for the slaves. By default PVMPOV
++            tries to run the slaves in the same directory, as the master.
++            But sometimes 'getcwd' gives misleading output (when automounting
++            is used), or you simply want to run the slaves in some other
++            directory, then you can use this option.
++
++     pvm_hosts=name1,name2,...
++            Set the names of the hosts to use for slaves. Note, that there are
++            not spaces allowed between the names!
++         
++            By default PVM distributes the processes in some order to the
++            available machines. Sometimes the choice of PVM is not the best,
++            so you can specify explicitly which machines to use. 
++            
++            You can use more tasks, then you specify here. You can use the
++            options in this way: pvm_hosts=darkstar,darkstar,baby +nt6
++            This will start 6 tasks (+nt6 must come after the pvm_hosts),
++            4 on darkstar and 2 on baby.
++   
++
++For example, if I wanted to distribute an 1024x768 rendering and use eight PVM 
++tasks to do the work, I would use the +NT8 option, and let the PVM master 
++define 768 32x32 sections.  Eight slave tasks would be launched, and when ready,
++they would request work to do from the master.  The master then issues a column
++and row range back to the requesting slave.
++
++% pvmpov +nt8 +w1024 +h768 +iskyvase.pov +oskyvase.tga
++
++As each quarter of a grid is completed, the slave sends it back to the
++master, which buffers the incoming blocks until an entire line recieved, at
++which time it writes the line to disk with the specified file format.  If
++all slaves did perfectly equal work in this example, they would be issued 96
++sections of work, and would have returned 384 messages of 32x8 pixels each.
++
++Note that the work issues are done essentially one at a time.  As each slave
++starts up it requests a single block, and as it recieves a work request, it 
++requests the next block.  In this way, effective load balancing can occur.
++Tasks rendering the "easy" sections will eventually all migrate towards the
++harder sections.  Seriously imbalanced distributions will be seen by widely
++varying percentages shown at the end of the rendering, assuming all of the
++hosts are identical.  Since each slave always has a block to be rendered, it
++will be kept busy without waiting for the master to recieve its work request
++and reply with a new block assignment.
++
++If for some reason any of the slaves are very slow, or they are interrupted
++during their operation, their blocks are reassigned to other slaves after
++all of the blocks have been assigned a first time.  This means that the render
++can be completed quickly even if some of the slaves have problems, or if some
++blocks have been assigned to slower slaves while the faster slaves are idle.
++This will show up as some percentage of late blocks for that machine in the
++statistics at the end.  Note that blocks will NOT be reassigned to slaves that
++are significantly slower than average, in preference to reassigning it to a
++faster slave when it becomes available.
++
++Since only complete lines are written out to disk, interrupted traces will
++be restarted at the last complete line that was written to disk, regardless
++of how many blocks were buffered by the master at the time.  However, it is
++possible to re-start an interrupted trace in the same method as POV-Ray.
++
++Known bugs (and other limitations)
++-------------------------------------
++All of the slaves must to share the same file system as the master process,
++and the source file must be mounted in the same location on each one.  This
++may limit the number of hosts that can be used in some circumstances.
++
++The command line arguments for specifying hosts is not cumulative, so
++
++% pvmpov +naSUNMP +nt24 +n +iskyvase.pov +oskyvase.tga
++
++will not start 24 slaves on the multi-processor SUN system(s) (which may have
++4 CPUs each), and then start 1 slave on each of the remaining systems.
++
++The statistics for the image being rendered may not be accurate if any of the
++slaves is too slow, or stops for some reason.  As well, since some sections
++may be rendered twice if the block is reassigned, the statistics will not be
++exactly the same as if it were rendered on a single host.  The master only
++waits a few seconds for statistics to arrive before printing them.
++
++The master does not parse the input file. Because of that it displays
++no error messages concerning the input file. As long as your input file
++still contains error you should disable PVM support (-N or pvm=off)
++
++Execution example (from version 2.9):
++-------------------------------------
++You first must have a PVM daemon launched on each machine that will be 
++participating in the rendering.  Refer to the PVM 3.3 documentation.  The 
++following is an example is from Jason Hough, and was generated on a group
++of six Solaris based 4-processor SPARCstation 20s.  His home directory is
++auto-mounted to all of these hosts.
++
++He keeps the pvm daemon installed in a directory called "bin", given by
++"dx=./bin/pvmd3" relative to his home directory, and pvmpov is in various
++subdirectories under "bin" (ie bin/SUN4, bin/SUNMP, bin/LINUX, etc.), given
++by the executable path "ex=./bin", so his pvm.hosts file looks like:
++
++% cat pvm.hosts
++glee    dx=./bin/pvmd3 ep=./bin
++elation dx=./bin/pvmd3 ep=./bin
++ecstasy dx=./bin/pvmd3 ep=./bin
++bliss   dx=./bin/pvmd3 ep=./bin
++delight dx=./bin/pvmd3 ep=./bin
++rapture dx=./bin/pvmd3 ep=./bin
++
++Note that, by default, PVM looks for executables in $HOME/pvm3/bin/$PVM_ARCH,
++and pvmd should be in $PVM_ROOT/lib, so if this is the case on your systems,
++you don't need to have anything in your pvm.hosts file except the hostnames,
++one per line.  An exception is RS6K, which needs the dx= no matter what.  See
++the PVM documentation and/or the pvmd3 man page for more information on what
++all of this means if you don't understand it.
++
++The following command launches the PVM daemons.
++
++% pvm pvm.hosts
++3.3.7
++t40001
++pvm> conf
++6 hosts, 1 data format
++                  HOST     DTID     ARCH   SPEED
++                  glee    40000    SUNMP    1000
++               elation    80000    SUNMP    1000
++               ecstasy    c0000    SUNMP    1000
++                 bliss   100000    SUNMP    1000
++               delight   140000    SUNMP    1000
++               rapture   180000    SUNMP    1000
++pvm> quit
++
++pvmd still running.
++
++Now that the PVM daemons are up and waiting for work to do, we can render.
++Note that for these MP machines he forces pvmpov to start more tasks than
++the default 1 per host, and uses a 64x64 block size:
++
++% pvmpov -Iskyvase.pov -Oskyvase.tga +nt24 +nw64 +nh64 +v
++
++POV-Ray Options in effect: +v1 +ft +mb25 +nt24 +nn5 +nw64 +nh64 +a0.300
+++j1.000 +b999 +r3 -q9 -w1024 -h768 -s1 -e768
++-k0.000 -mv2.0 -iskyvase.pov -oskyvase.tga 
++   ...at least 13 tasks successfully spawned in time.
++   ...Don't worry, more are on the way, I'm just not waiting
++PVM Task Distribution: Tasks-24  Grid width-64  Grid height-64  Sections-192
++
++Waiting for slave stats.
++
++PVM Task Distribution Statistics:
++           host name  [ done ] [ late ]           host name  [ done ] [ late ]
++                glee  [ 4.17%] [ 0.00%]                glee  [ 4.17%] [ 0.00%]
++                glee  [ 4.17%] [ 0.00%]                glee  [ 4.17%] [ 0.00%]
++             elation  [ 4.69%] [ 0.00%]             elation  [ 4.17%] [ 0.00%]
++             elation  [ 4.17%] [ 0.00%]             elation  [ 4.17%] [ 0.00%]
++             ecstasy  [ 3.65%] [ 0.00%]             ecstasy  [ 4.69%] [ 0.00%]
++             ecstasy  [ 4.69%] [ 0.00%]             ecstasy  [ 4.17%] [ 0.00%]
++               bliss  [ 3.65%] [ 0.00%]               bliss  [ 4.17%] [ 0.00%]
++               bliss  [ 4.69%] [ 0.00%]               bliss  [ 3.65%] [ 0.00%]
++             delight  [ 3.65%] [ 0.00%]             delight  [ 4.17%] [ 0.00%]
++             delight  [ 4.17%] [ 0.00%]             delight  [ 4.17%] [ 0.00%]
++             rapture  [ 4.69%] [ 0.00%]             rapture  [ 4.17%] [ 0.00%]
++             rapture  [ 4.17%] [ 0.00%]             rapture  [ 3.65%] [ 0.00%]
++
++
++skyvase.pov statistics
++--------------------------------------
++Resolution 1024 x 768
++# Rays:     3773743    # Pixels:      798720  # Pixels supersampled:      17381
++  Ray->Shape Intersection Tests:
++   Type             Tests    Succeeded   Percentage
++  -----------------------------------------------------------
++  Sphere          6304452     1170727       18.57
++  Plane          63822062    35385552       55.44
++  Quadric         6304452     2770858       43.95
++  Cone            5918163     4839298       81.77
++  Bounds          5918163     3152226       53.26
++  Calls to Noise:      4327871
++  Calls to DNoise:     5141872
++  Shadow Ray Tests:   10498615     Blocking Objects Found:      254807
++  Reflected Rays:      2818594
++  Time For Trace:    0 hours  0 minutes 47.00 seconds
++
++------
++NB: Note that for comparison purposes with other skyvase benchmarks that this
++    is rendered at 1024x768 instead of the usual 640x480!
++
++Enjoy!
++
++Andreas Dilger
++adilger@enel.ucalgary.ca
++
++Harald Deischinger
++k3096e5@c210.edvz.uni-linz.ac.at
+diff -Naur source.ori/pvm/PVMPOV.general source/pvm/PVMPOV.general
+--- source.ori/pvm/PVMPOV.general      Thu Jan  1 01:00:00 1970
++++ source/pvm/PVMPOV.general  Sun Sep 12 00:25:35 1999
+@@ -0,0 +1,98 @@
++
++ - PVMPOV -                             written by Andreas Dilger, June 1 1995
++                                                  and Harald Deischinger, 1997
++                                                modified by Jakob Flierl, 1999
++
++
++*****      NOTE - This is an UNOFFICIAL modification to POV-Ray 3.1e       *****
++
++** DO NOT SEND PROBLEM REPORTS TO THE POV TEAM REGARDING THESE MODIFICATIONS. **
++
++The author disclaims all warranties with regard to this software, including 
++all implied warranties of merchant-ability and fitness.  The code is simply 
++distributed as it is. 
++
++
++General information
++-------------------
++
++"I only ask that credit (or blame) is attributed to me for the original work."
++                                                             - Brad Kline
++
++The original author of the PVM POV-Ray patch on which this is based is Brad
++Kline, of Cray Research, Inc.  Because of problems running the original code at
++my site, and a desire to learn more about PVM for my Master's work, I have
++changed large portions to not only be more user-friendly about reporting
++errors, but also hopefully more robust and faster.
++                                                         - Andreas Dilger
++
++Because Andreas has other things to do, I have taken over the part of
++PVMing POV-Ray 3.1. If you have problems with this program, don't be afraid
++to ask me for help. 
++
++Please send me a mail, if you're using PVMPOV Version 3.1e.2. 
++                                                           - Jakob Flierl
++Contact
++-------
++email    : flierl@luga.de
++
++Important information/Problems
++------------------------------
++
++* This version is not tested very vell, so it is very likely, that you
++  find some errors - please tell me about them.
++
++  Especially the very time consuming new features (radiosity) are not
++  really tested.
++
++* Shellout commands are always done by the master process.
++
++* Now PVMPOV also supports animations. PVMPOV computes multiply frames
++  simultaniously, so it is not possible to do "recursive animations"
++  (like the desk example in povscn/level3). You have to run PVMPOV extra
++  for each frame.
++
++  All the pre-scene shellouts are done in the correct order, and the same
++  for the post-scene shellouts. BUT for example the pre-scene shellout for 
++  frame 2, will be executed before the post-scene shellout for frame 1!
++
++  Field Rendering (+UF) is not working yet.
++
++* The X11 display now displays everything. 
++  There are no longer "forgotten blocks".
++
++* The master process of PVMPOV does not parse the input file. So you will
++  NOT see errors in the .pov file - the slaves will simply not start.
++  So you should turn off PVM support (pvm=off or -N) while developing your
++  scene file.
++
++  You will also get no warning, if the slave can not find the input file.
++  The slave will simply never ask for work. Check "/tmp/pvml.<uid>" for
++  errors with the slaves.
++
++* You might get errors, that pvm can not find the executables for the slaves.
++  You can solve this in lot's of ways. 
++  - Start pvmpov with an absolute filename,
++    e.g. '/usr/bin/pvmpov ...'
++  - Specify the absolute path for the slaves, 
++    e.g. 'pvmpov -NS/usr/bin/pvmpov ...'
++    You can set this option in you '.povrayrc' file.
++  - Move your executables into ~/pvm3/bin/ARCH
++  - Use the 'ep=PATH' option in your hostfile 
++  Read the PVM documentation for more information.
++
++* When using automount you might have the problem, that the slaves can't
++  change to the working directory. Use the +ND (pvm_wd) option.
++  Calling pvmpov with "+ND$PWD" should solve the problem.
++
++* For more information see "PVMPOV.1st_scene"
++
++Changed files
++-------------
++   optin.c             Accept new options for PVMPOV
++   optin.h
++   optout.c            Print new options and help texts for PVMPOV
++   optout.h            
++   povray.c            Major changes
++   render.c            Minor changes
++   render.h
+diff -Naur source.ori/pvm/PVMPOV.install source/pvm/PVMPOV.install
+--- source.ori/pvm/PVMPOV.install      Thu Jan  1 01:00:00 1970
++++ source/pvm/PVMPOV.install  Sun Sep 12 00:25:35 1999
+@@ -0,0 +1,39 @@
++How to compile and install PVMPOV. 
++
++COMPILE
++-------
++Before compiling PVMPOV you must me sure to have PVM installed correctly.
++PVM is not included with this package. You have to download and install it
++manually: http://www.epm.ornl.gov/pvm/pvm_home.html
++
++After that you can compile and install PVMPOV:
++
++  tar xvfz pvmpov-3.1e.?.tgz <- unpack the PVMPOV distfiles
++  cd pvmpov3_1e_?
++  tar xvfz ~/povuni_s.tgz    <- unpack the POV source from ftp://ftp.povray.org/
++[POV-Ray versions 3.1e - 3.1g are reported to work fine with PVMPOV 3.e1.2.]
++
++  ./inst-pvm                 <- apply the PVMPOV patch
++
++[This script applies the patch and searches for any rejected files.
++If there are any .rej-files the changes are not done correctly - 
++you can try to do the changes by hand.]
++
++  cd pvmpov3_1e_?/povray31/source/pvm
++
++[You can choose:]
++  aimk newunix               <- to create the UNIX text only binaries
++  aimk newsvga               <- to create binaries for svgalib (Linux only)
++  aimk newxwin               <- to create binaries using X11 to display
++
++This should create the executables 'pvmpov', 's-pvmpov' or 'x-pvmpov'.
++
++INSTALL
++------
++If your are running PVMPOV on a homogenous cluster, you can copy the
++executables to "/usr/bin" and "/usr/X11R6/bin".
++
++Read "PVMPOV.example" to get started.
++
++Jakob Flierl, flierl@luga.de
++May 1999
+diff -Naur source.ori/pvm/PVMPOV.radiosity source/pvm/PVMPOV.radiosity
+--- source.ori/pvm/PVMPOV.radiosity    Thu Jan  1 01:00:00 1970
++++ source/pvm/PVMPOV.radiosity        Sun Sep 12 00:25:35 1999
+@@ -0,0 +1,39 @@
++[...] PVMPOV and radiosity:
++
++Radiosity is currently not working with PVMPOV; the resulting images look like
++mosaics. There's a currently only a workaround suggested by Andreas Dilger:
++
++You must create a .rca file for the whole image [when rendering with the
++radiosity option turned on], which I did manually by running
++"pvmpov -n +qr +d +i <scene>.pov" and stopping the rendering when the mosaic
++preview is complete, to give me <scene>.rca. Then I started a new
++"pvmpov +c +qr +d +i <scene>.pov +nt2" on my 2 CPU system, and it rendered
++the same image as letting "pvmpov -n +qr +d +i <scene>.pov" finish on 1 CPU.
++The <scene>.rca file was readable by both slave systems because they were local,
++but it would also be possible to use NFS for this, like normal PVMPOV.
++This is some work to do manually, but it will ensure that the radiosity
++will work properly with PVMPOV.
++
++[for programmers only:]
++What needs to be done now is to have the PVMPOV master run the mosaic
++preview for radiosity before the slave tasks start.  It looks like this
++could be done in pvm.c:PvmStartFrame(), but the current design of the
++master doesn't allow it to do any rendering because it does not parse
++the scene, so it would need to be reworked to do this.
++
++In the old PVMPOV 2.2 design, the master did the parsing of the scene
++file to ensure that the syntax was correct.  I think this is the better
++design.  If radiosity is used, the master will render the preview before
++the rendering is started on the slaves, so the .rca file is available.
++If the master does the parsing, then it can also send the data to the
++slaves via the POB binary file format through PVM (no NFS needed) so we
++don't waste the time spent parsing the scene at the master.
++
++I think what needs to be done is modify povray.c so it doesn't call
++Pvm_Master_Control() instead of FrameRender() in povray.c:398.  Then
++FrameRender() should be modified to work with PVMPOV code, replacing
++PvmStartFrame() entirely.  This will mean that PVMPOV will render one
++frame at a time, which is OK I think, because it means that some of
++the problems with PVMPOV 3.x will be fixed, like post-scene shellouts.
++I think it will also make PVMPOV less complex also, because it only
++has to work on 1 frame at a time.
+diff -Naur source.ori/pvm.c source/pvm.c
+--- source.ori/pvm.c   Thu Jan  1 01:00:00 1970
++++ source/pvm.c       Sun Sep 12 00:25:35 1999
+@@ -0,0 +1,1556 @@
++/****************************************************************************
++*  pvm.c
++*
++*  This module implements the PVM control routines for PVM'd POVRAY.
++*
++*  This file was written by Brad Kline.  April 1994.
++*                modified by Andreas Dilger Jan - May 1995
++*                modified by Harald Deischinger, 1996 - 1997
++*
++*  NOTE - PVM is not supplied here.
++*
++*  You need the PVM 3.x libraries, include files, and PVMD daemons.
++*  
++*  PVM is available from the University of Tennessee, Knoxville, TN.
++*
++*  The author disclaims all warranties with regard to this software, 
++*  including all implied warranties of merchant-ability and fitness. 
++*  The code is simply distributed as it is.
++*
++* 970610 - New option PVM_WD. To specify a working directory for
++*          the slaves. [Deischi]
++*          New option PVM_Hosts. To give names for hosts to use
++*          as slaves. [Deischi]
++*
++* 9704.. - changed to to animations, lots of changes
++*          simplifications to the whole thing [Deischi]
++*
++* 96.... - Changed code to work with POVRAY 3.0 [Deischi]
++*
++* 950508 - Changed code that allocates blocks to be more robust.
++*          Changed the write_line routines to hook into the existing routines.
++*          Changed the slave startup processes to be more informative.
++*
++* 940511 - Remove code that tried to fit grid sections into whole parts. The
++*          code now supports odd sized images.
++*
++*****************************************************************************/
++#include <sys/time.h>
++#include <sys/resource.h>
++#include "frame.h"
++#include "vector.h"
++#include "povproto.h"
++#include "frame.h"
++#include "povray.h"
++#include "render.h"
++#include "optout.h"
++#include "pvm3.h"
++#include "pvm.h"
++
++
++/* This distribution scheme breaks down the image into to two dimensional
++ * areas.  Each area has a designated grid control point.  The grid area
++ * is up to PvmChunkWidth (-NWxx) pixels wide by PvmChunkHeight (-NHxx)
++ * high. If one uses the default, then a grid section would be 32x32, or
++ * 1024 pixels.  In a 640x480 image, this would create 300 grid control
++ * points, and therefore 300 different sections to do work on (not lines.).
++ */
++
++/*
++ * clock value in shellout command is not working correctly
++ * timings are not displayed (and done) correctly
++ * statistics can be wrong
++ * continue trace might produce wrong results -- needs testing
++ */
++
++/*
++ * Global variables
++ */
++char    PvmArch[20] = { 0 };
++int     PvmChunkHeight = PVM_DEFAULT_GRID_HEIGHT;     /* height of a block */
++int     PvmChunkWidth  = PVM_DEFAULT_GRID_WIDTH;      /* widht of a block */
++int     PvmRowsToSend = 1;
++int     PvmSlave = 0;
++int     PvmTasks = 9999;
++int     PvmNice = PVM_DEFAULT_NICE;
++int     PvmMTid;
++int     PvmBlockNum;
++char *  PvmSlavename = NULL;
++int   PvmNextFrame = 0;
++char  PvmWorkingDir[PATH_MAX+1] = "";                 /* PVM Working Directory */
++char ** PvmHosts = NULL;
++int   PvmHostsN = 0;
++
++/*
++ * local functions
++ */
++static void PvmSpawnError PARAMS((int));
++static void PvmSpawnTidError PARAMS((int));
++static void PvmSpawn PARAMS((void));
++static void PvmCheckFirst PARAMS((void));
++static void PvmInitSlaveStat PARAMS((void));
++static void PvmInitFrameStat PARAMS((void));
++
++static int PvmMasterReceive PARAMS((void));
++static void PvmIdentifySlave PARAMS((void));
++static void PvmSendWork PARAMS((int block));
++static void PvmReassignWork PARAMS((int block));
++static void PvmAssignWork PARAMS((void));
++static void PvmMasterEnd PARAMS((void));
++static int PvmStartFrame PARAMS((void));
++static void PvmFinishFrame PARAMS((void));
++static void PvmReceiveData PARAMS((void));
++static void PvmWrite PARAMS((void));
++
++static void PvmUnpackStats PARAMS((void));
++
++
++static int grid_points;               /* number of grid blocks for one frame */
++static int grid_ppline;               /* Number of grid blocks in a row */
++static int frames;            /* num. of frames to do */
++static int frame = -1;                /* the current frame, start with 0 to count */
++static int next_frame_finish; 
++static int frame_display;     /* the frame that is displayed */
++
++static int grid_lines_done = 0;       /* num. of PvmCthunkWidth lines done (only statistics) */
++
++static int npixels_done = 0;    /* Number of pixels done, by all processors */
++
++static int                row_start;           /* First row in the block */
++static int                row_end;             /* Last row in the block */
++static int                col_start;           /* First column in the block */
++static int                col_end;             /* Last column in the block */
++static int msgtag; 
++
++static   struct timeval     wait_time;           /* Time to wait for slave stats */
++static   struct pvmhostinfo *hostinfo;           /* Info on each PVM host */
++static   int                minions;             /* Number of slave tasks */
++static   int                nhost;               /* Number of available pvm hosts */
++static   pvm_slave_stat     *slave_stat;         /* Info on the slaves */
++static   pvm_frame_stat     *frame_stat;
++static   int                rtid;                /* tid of the message sender */
++static   int                *tids;               /* tids of all the slaves */
++static   int                slave_num;           /* Relative host number of sender */
++static   char               *slave_name;         /* Hostname of sending slave */
++static   int                nstat = 0;           /* Number of slaves returning stats */
++
++static   int                bitmask;             /* Start tasks on a specific arch */
++static   int                bufid;               /* pvm recieve buffer id */
++static   int                bytesin;             /* Size of message */
++static   char               **sargs;             /* argv for the slaves */
++static   char               slave_option[PATH_MAX+4]; /* the directory argument */
++
++
++/* in povray.c */
++extern char Actual_Output_Name[FILE_NAME_LENGTH];
++void setup_output_file_name PARAMS((void));
++void open_output_file PARAMS((void));
++void set_output_file_handle();
++
++
++
++/********************************************************************************
++ * Initialize Master Process
++ *******************************************************************************/
++
++
++/*
++ * Spawn the slaves, and check for errors 
++ */
++
++/* handle an error from pvm_spawn */
++static void PvmSpawnError(m) 
++    int m; 
++{
++    /* Only quit if we can't start many tasks, or an error condition. */
++    Error_Line( "...error spawning tasks because \n");
++    switch (m) {
++    case PvmBadParam:
++      Error_Line("of bad parameter.\n");
++      break;
++    case PvmNoHost:
++      Error_Line("host not in PVM.\n");
++      break;
++    case PvmNoFile:
++      Error_Line("executable not found.\n");
++      break;
++    case PvmNoMem:
++      Error_Line("no memory on host.\n");
++      break;
++    case PvmSysErr:
++      Error_Line("pvmd not responding.\n");
++      break;
++    case PvmOutOfRes:
++      Error_Line("lack of resources.\n");
++      break;
++    default:
++      Error_Line("of unknown error.\n");
++    }
++    pvm_perror("");
++    pvm_exit();
++    close_all();
++    exit(1);
++}
++
++static void PvmSpawnTidError(tid) 
++    int tid; 
++{
++
++    Warning(0.0, "...spawn failure because ");
++    switch (tid) {
++    case PvmBadParam:
++      Warning(0.0, "of bad parameter.\n");
++      break;
++    case PvmNoHost:
++      Warning(0.0, "host not in PVM.\n");
++      break;
++    case PvmNoFile:
++      Warning(0.0, "executable not found.\n");
++      break;
++    case PvmNoMem:
++      Warning(0.0, "no memory on host.\n");
++      break;
++    case PvmSysErr:
++      Warning(0.0, "pvmd not responding.\n");
++      break;
++    case PvmOutOfRes:
++      Warning(0.0, "lack of resources.\n");
++      break;
++    default:
++      Warning(0.0, "of unknown error.\n");
++    }
++}
++
++static void PvmSpawn() {
++    int i;
++
++    /* Start up the slave processes */
++    if (PvmArch[0] != '\0') {
++      Status_Info("  Spawning %s with %d PVM tasks on arch %s...\n",
++                  PvmSlavename, PvmTasks,PvmArch);
++    } else {
++      Status_Info("  Spawning %s with %d PVM tasks on %d hosts...\n",
++                  PvmSlavename, PvmTasks,nhost);
++    }
++
++    if(PvmHosts) {                    /* start at specified hosts */
++      int m;
++      minions = 0;
++      for(i=0; i < PvmTasks; i++) {   /* start one by one */
++          m = pvm_spawn(PvmSlavename, sargs, PvmTaskHost, PvmHosts[i % PvmHostsN], 
++                        1, tids+minions);
++          if( m < 0) {                        /* spawn failed */
++              PvmSpawnError(m);
++          } else if (m==0) {                  /* failed a little bit */
++              PvmSpawnTidError(tids[minions]);
++          }
++          minions += m;
++      }
++
++    } else {                          /* start all at once somwhere */
++      minions = pvm_spawn(PvmSlavename, sargs, bitmask, PvmArch, PvmTasks, tids);
++
++      if(minions < 0)                 /* check for total failure */
++          PvmSpawnError(minions);
++      for (i = minions; i < PvmTasks; i++)    /* errors/process */
++          PvmSpawnTidError(tids[i]);
++    }
++
++    /* Check if "few" tasks could be started */
++    if (minions < (PvmTasks + 1) / 2)  {
++      if(minions==0) {
++          Error_Line("...No tasks");
++        } else {
++          Error_Line("...only %d of %d tasks", minions, PvmTasks);
++      }
++      Error_Line(" spawned!  Quitting.\n");
++      pvm_exit();
++      close_all();
++      exit(1);
++    } else {
++      Status_Info("  ...%d PVM tasks successfully spawned.\n", minions);
++    }
++
++    PvmTasks = minions;
++}
++
++
++/*
++ * Wait up to 120 seconds for the first to check in
++ */
++static void PvmCheckFirst() {
++    wait_time.tv_sec = 120;
++    wait_time.tv_usec = 0;
++
++    Status_Info("  Waiting up to %lds for first slave to start...\n",
++              wait_time.tv_sec);
++    minions = 0;
++
++    if( pvm_trecv(-1, PVM_INIT_SLAVE, &wait_time) <= 0 ) {
++      /* No slave checked in. Something is wrong */
++      Error_Line("First slave did not start in time!  Quitting.\n");
++      Error_Line("See /tmp/pvml.<your uid> on the master PVM host for\n");
++      Error_Line("more info on why the slave didn't start correctly.\n");
++      pvm_exit();
++      close_all();
++      exit(1);
++    }  else {
++      minions++;
++    }
++
++    if (opts.Options & VERBOSE) {
++      Status_Info("  Slave 0 successfully started.\n");
++    }
++}
++
++
++/* 
++ * Get a status struct for each slave 
++ */
++static void PvmInitSlaveStat() {
++    
++    slave_stat = (pvm_slave_stat *)POV_CALLOC(PvmTasks, sizeof(pvm_slave_stat), "slave_stat");
++    
++    /* Set up pointers to all the host names and the slave status */
++    for (slave_num = 0; slave_num < PvmTasks; slave_num++) {
++      int j;
++      int dtid;
++
++      dtid = pvm_tidtohost(tids[slave_num]);
++      slave_stat[slave_num].name = "unknown";
++
++      for (j = 0; j < nhost; j++) {
++          if (dtid == hostinfo[j].hi_tid) {
++              slave_stat[slave_num].name = (char*)POV_CALLOC(strlen(hostinfo[j].hi_name)+1, 
++                                                             sizeof(char), "hostnames");
++              strcpy(slave_stat[slave_num].name,hostinfo[j].hi_name);
++              break;
++          }
++      }
++      slave_stat[slave_num].frame_assigned = PVM_SLAVE_FREE;
++      slave_stat[slave_num].block_reassigned = PVM_SLAVE_FREE;
++    }
++}
++
++/* 
++ * Get a status struct for each frame
++ */
++static void PvmInitFrameStat() {
++    int i,j;
++
++    frames = (opts.FrameSeq.FrameType == FT_MULTIPLE_FRAME) ? 
++      opts.FrameSeq.FinalFrame - opts.FrameSeq.InitialFrame + 1 :
++      1;
++    frame_stat = (pvm_frame_stat *)POV_CALLOC(frames, sizeof(pvm_frame_stat), "frame_stat");
++    frame_display = -1;
++
++    for(i=0; i < frames; i++) {
++      frame_stat[i].block_to_assign = 0;
++      frame_stat[i].block_to_reassign = 0;
++
++      frame_stat[i].block_to_write = 0;
++
++      frame_stat[i].row_to_write  = opts.First_Line;
++      frame_stat[i].row_to_malloc = opts.First_Line;
++
++      frame_stat[i].block_stat = 
++          (pvm_block_stat *)POV_CALLOC(grid_points, sizeof(pvm_block_stat), "block_stat");
++
++      for(j=0; j < MaxStat; j++)
++          Init_Counter(frame_stat[i].stats[j]);
++    }
++}
++
++/*
++ * Initialize PVM Master
++ */
++void 
++Pvm_Master_Init(argc, argv)
++    int     argc;
++    char    **argv;
++{
++    int                i;
++
++    Status_Info("Initializing PVMPOV\n");
++
++    /* Get a table for the TIDs */
++    tids = (int *)POV_CALLOC(PvmTasks, sizeof(int), "tids");
++
++    /* Build a new arg list for the sub-tasks - add -ni in front */
++    sargs = (char **)POV_CALLOC(argc + 1, sizeof(char *), "sargs");
++
++    /* Move argument pointers to new list array, keep position 0 free for -ni option */
++    for (i = 1; i < argc; i++) {
++      sargs[i] = argv[i];
++    }
++
++    /* Get current directory for the slave tasks */
++    if(PvmWorkingDir[0] == '\0')
++      if ( GETCWD(PvmWorkingDir) == NULL) {
++          Error("PVM Master cannot get current directory.\n");
++          return;
++      }
++
++    /* Add slave flag */
++    sprintf(slave_option, "-nI%s", PvmWorkingDir);
++    sargs[0] = slave_option;
++
++    /* Enroll in PVM */
++    if (pvm_mytid() < 0) {
++      Error("Error starting master task.\n");
++    }
++
++    if (pvm_config(&nhost, (int *)NULL, &hostinfo) < 0) {
++      pvm_exit();
++      Error("Error getting PVM machine status.\n");
++    }
++           
++    bitmask = PvmTaskDefault;
++
++    /* If PvmArch is specified, use that architecture */
++    if (PvmArch[0] != '\0') {
++      bitmask = PvmTaskArch;
++
++      /* If arch is psecified, but not the number of tasks, go and
++       * count how many hosts of that type there are.
++       */
++      if (PvmTasks >= 9999) {
++          PvmTasks = 0;
++          
++          for (i = 0; i < nhost; i++) {
++              if (strcmp(PvmArch,hostinfo[i].hi_arch) == 0)
++                  PvmTasks++;
++          }
++      }    
++    }
++
++    /* If PvmTasks is specified, use that many tasks, otherwise use
++     * as many hosts as possible.
++     */
++    if (PvmTasks >= 9999) {
++      PvmTasks = nhost;
++    }
++
++    /* if no slavename was given, use name of current executable */
++    if(PvmSlavename == NULL)
++      PvmSlavename = argv[0];
++
++    PvmSpawn();                                               /* spawn the slaves */
++    PvmCheckFirst();                                  /* wait for first slave */
++
++    pvm_setopt(PvmRoute, PvmRouteDirect);
++
++    /* Max # of pixels in a grid row */
++    PvmChunkWidth = min(PvmChunkWidth, opts.Last_Column - opts.First_Column);
++
++    /* Grid points per line - rounded up */
++    grid_ppline = (opts.Last_Column  - opts.First_Column + PvmChunkWidth - 1) / PvmChunkWidth;
++
++    /* Establish grid section height */
++    PvmChunkHeight = min(PvmChunkHeight, opts.Last_Line - opts.First_Line);
++
++    /* Total grid points in the image - round up PvmChunkHeight */
++    grid_points = ((opts.Last_Line - opts.First_Line + PvmChunkHeight - 1) / PvmChunkHeight) * 
++      grid_ppline;
++    
++    PvmInitSlaveStat();
++    PvmInitFrameStat();
++
++    /* initialize total stats */
++    for(i=0; i < MaxStat; i++) {
++      Init_Counter(totalstats[i]);
++    }
++}
++
++
++
++
++
++/********************************************************************************
++ * Master Control
++ ********************************************************************************/
++
++/*
++ * Receive a message
++ */
++static int PvmMasterReceive() {
++
++    /* wait for a message from the slaves */
++    if ((bufid = pvm_trecv(-1, -1, &wait_time)) < 0) {
++      Error_Line( "PVM Master cannot receive.\n");
++      pvm_perror("");
++      pvm_exit();
++      exit(1);
++    }
++
++    /* We timed out waiting for any messages */
++    if (bufid == 0) {
++      Status_Info(".");
++      fflush(stderr);
++      
++      return 1;
++    }
++    
++    return 0;
++}
++
++/*
++ * Find name and number of slave, of current message
++ */
++static void PvmIdentifySlave() {
++
++    /* get info about this msg. */
++    if (pvm_bufinfo(bufid, &bytesin, &msgtag, &rtid) < 0) {
++      Error_Line( "PVM Master cannot bufinfo.\n");
++      pvm_perror("");
++      pvm_exit();
++      return;
++    }
++
++    /* find the name of the slave, sending the msg. */
++    slave_name = "unknown";
++    for (slave_num = 0; slave_num < PvmTasks; slave_num++) {
++      if (tids[slave_num] == rtid) {
++          slave_name = slave_stat[slave_num].name;
++          break;
++      }
++    }
++}
++
++/*
++ * Send a PVM_WORK message
++ * Set block to PVM_GRID_POINT_ASSIGNED
++ */
++static void PvmSendWork(block) 
++    int block;
++{
++    int row_start, row_end, col_start, col_end;
++#ifdef DEBUG
++    Status_Info("Assigning block: %d, frame: %d of %d to %s.\n", block, frame, frames, slave_name);
++#endif /* DEBUG */
++
++    frame_stat[frame].block_stat[block].status = PVM_GRID_POINT_ASSIGNED;
++    frame_stat[frame].block_stat[block].assigned_tid = rtid;
++    
++    row_start = (block/grid_ppline) * PvmChunkHeight + opts.First_Line;
++    row_end   = (row_start + PvmChunkHeight) - 1;
++          
++    if (row_end >= opts.Last_Line)
++      row_end = opts.Last_Line - 1;
++    
++    col_start = (block%grid_ppline) * PvmChunkWidth + opts.First_Column;
++    col_end   = (col_start + PvmChunkWidth) - 1;
++    
++    if (col_end >= opts.Last_Column)
++      col_end = opts.Last_Column - 1;
++    
++    pvm_initsend(PvmDataDefault);
++    pvm_pkint(&frame,1,1);
++    pvm_pkint(&block,1,1);
++    pvm_pkint(&row_start,1,1);
++    pvm_pkint(&row_end,1,1);
++    pvm_pkint(&col_start,1,1);
++    pvm_pkint(&col_end,1,1);
++    if (pvm_send(rtid, PVM_WORK) < 0) {
++      Error_Line( "PVM Master can't send to slave on %s.\n",slave_name);
++      pvm_perror("");
++      pvm_exit();
++      exit(1);
++    }
++
++    frame_stat[frame].block_to_assign ++;
++}
++
++/*
++ * Send a PVM_WORK message
++ * Set block to PVM_GRID_POINT_REASSIGNED
++ */
++static void PvmReassignWork(block) 
++    int block;
++{
++#ifdef DEBUG
++    Status_Info("Reassigning %d to %s.\n", block,
++              slave_name);
++#endif /* DEBUG */
++    
++    frame_stat[frame].block_stat[block].status=PVM_GRID_POINT_REASSIGNED;
++    frame_stat[frame].block_stat[block].reassigned_tid = rtid;
++    slave_stat[slave_num].block_reassigned = block;
++                      
++    if (frame_stat[frame].block_stat[block].wr_line == 0) {
++      row_start = (block / grid_ppline)*PvmChunkHeight +  opts.First_Line;
++    } else {
++      row_start = frame_stat[frame].block_stat[block].wr_line;
++    }
++    row_end = (block/grid_ppline + 1) * PvmChunkHeight - 1;
++    
++    if (row_end >= opts.Last_Line)
++      row_end = opts.Last_Line - 1;
++                      
++    col_start = (block % grid_ppline) * PvmChunkWidth +
++      opts.First_Column;
++    col_end   = (col_start + PvmChunkWidth) - 1;
++    
++    if (col_end >= opts.Last_Column)
++      col_end = opts.Last_Column - 1;
++    
++    pvm_initsend(PvmDataDefault);
++    pvm_pkint(&frame,1,1);
++    pvm_pkint(&block,1,1);
++    pvm_pkint(&row_start,1,1);
++    pvm_pkint(&row_end,1,1);
++    pvm_pkint(&col_start,1,1);
++    pvm_pkint(&col_end,1,1);
++    if (pvm_send(rtid, PVM_WORK) < 0) {
++      Error_Line( "PVM Master can't send to slave on %s.\n",slave_name);
++      pvm_perror("");
++      pvm_exit();
++      exit(1);
++    }
++
++    frame_stat[frame].block_to_reassign++;
++}
++
++
++/*     
++ * Do all the stuff, that is necessary for a new frame 
++ * This is called before the PVM_WORK is sent
++ *
++ * A lot of this is taken from 'FrameRender' 
++ *
++ * ret: 0 .. OK send the PVMWOR 
++ *      1 .. Shellout gave SKIP_ONCE_RET -> this frame is done
++ */
++static int PvmStartFrame() {
++    int Frame_Result;
++
++    if(frame_stat[frame].status != PVM_FRAME_FREE) {
++      Warning(0.0, "Frame %d already started.\n", frame);
++      return 1;
++    }
++
++    if(opts.Options & VERBOSE)
++      Status_Info("\nStarting frame %d...", opts.FrameSeq.InitialFrame + frame);
++    
++    frame_stat[frame].status = PVM_FRAME_WORKING;
++
++    /* allocate row pointers */
++    frame_stat[frame].row_ptr = (COLOUR **)POV_CALLOC(opts.Last_Line, sizeof(COLOUR *), "row_ptr");
++
++    opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame + frame;
++    setup_output_file_name();
++    
++    /* Execute a shell-out command before tracing */
++    Frame_Result = POV_SHELLOUT(PRE_FRAME_SHL);
++    
++    if (Frame_Result == ALL_SKIP_RET) {
++      /* stop completely */
++      int i;
++      for(i=frame; i < frames; i++) {
++          frame_stat[i].block_to_assign = grid_points;
++          frame_stat[i].block_to_reassign = grid_points;
++          frame_stat[i].status = PVM_FRAME_DONE_SHELLOUT;
++      }
++      /* continue searching, frames before this one, should be finished */
++      return 1;               
++    }
++    
++    if (Frame_Result == SKIP_ONCE_RET) {
++      /* mark frame as done */
++      frame_stat[frame].block_to_assign = grid_points;
++      frame_stat[frame].block_to_reassign = grid_points;
++      frame_stat[frame].status = PVM_FRAME_DONE_SHELLOUT;
++
++      /* continue searching */
++      return 1;
++    }
++
++    /*
++     * open output file
++     */
++    set_output_file_handle();
++    open_output_file();
++
++    Initialize_Renderer(); 
++
++    /*
++     * Start displaying
++     */
++    if( (opts.Options & DISPLAY) && !Display_Started)  {
++      Status_Info("\nDisplaying frame %d...", opts.FrameSeq.InitialFrame + frame);
++      POV_DISPLAY_INIT(Frame.Screen_Width, Frame.Screen_Height);
++      Display_Started = TRUE;
++      frame_display = frame;
++      Status_Info("\n");
++    }
++    
++    
++    /*
++     * read already rendered parts
++     */
++    if ((opts.Options & DISKWRITE) && (opts.Options & CONTINUE_TRACE)) {
++      int i;
++      int fl = opts.First_Line;                       /* remember start of image */
++      
++      Read_Rendered_Part(Actual_Output_Name);         /* read -> changes opts.First_line */
++
++      frame_stat[frame].row_to_write = 
++          frame_stat[frame].row_to_malloc = opts.First_Line;
++
++      frame_stat[frame].block_to_write = 
++          frame_stat[frame].block_to_assign = (opts.First_Line/PvmChunkHeight)*grid_ppline;
++      /* blocks above the new start are already finished */
++      for(i=0; i < frame_stat[frame].block_to_assign; i++) {
++          frame_stat[frame].block_stat[i].status = PVM_GRID_POINT_WRITTEN;
++      }
++      if(opts.Options & VERBOSE)
++          Status_Info("Continuing with block %d in frame %d\n", 
++                      frame_stat[frame].block_to_assign, frame);
++
++      opts.First_Line = fl;                           /* set back the old value */
++
++
++      /* this should not be necessary */
++      if (opts.Last_Line > Frame.Screen_Height)
++          opts.Last_Line = Frame.Screen_Height;
++      
++      if (opts.Last_Column > Frame.Screen_Width)
++          opts.Last_Column = Frame.Screen_Width;      
++    }
++
++    /*
++     *  make a copy of the Output_File_Handle 
++     */
++    frame_stat[frame].output_file = Output_File_Handle;
++    if(Output_File_Handle->filename) {
++      char * n = POV_MALLOC(strlen(Output_File_Handle->filename)+1, "filename");
++      strcpy(n, Output_File_Handle->filename);
++      frame_stat[frame].output_file->filename = n;
++    }
++
++    if(opts.Options & VERBOSE)
++      Status_Info("\n");
++
++    return 0;
++}
++
++
++/*
++ * test, if the next frame in the sequence finished,
++ * do all the necessary stuff to close the frame (shellout, ...)
++ */
++static void PvmFinishFrame() {
++
++    /* check, if frame has really finished */
++    if( frame_stat[frame].row_to_write < opts.Last_Line)
++      return;
++
++    /* if not already closed */
++    if( frame_stat[frame].status < PVM_FRAME_DONE) {
++      
++      if(opts.Options & VERBOSE)
++          Status_Info("\nFinishing frame %d...", opts.FrameSeq.InitialFrame + frame);
++      Status_Info("rtw. %d\n", frame_stat[frame].row_to_write);
++
++      frame_stat[frame].status = PVM_FRAME_DONE;
++      
++      POV_FREE(frame_stat[frame].row_ptr);
++      
++      /* Close output file */
++      if (frame_stat[frame].output_file) {
++          Output_File_Handle = frame_stat[frame].output_file;
++          Close_File(frame_stat[frame].output_file);
++      }
++      
++      if( frame == frame_display) {
++          POV_DISPLAY_FINISHED
++              
++              if ((opts.Options & DISPLAY) && Display_Started)  {
++                  POV_DISPLAY_CLOSE
++          
++                      Display_Started = FALSE;
++              }
++          
++          frame_display = -1;
++      }
++      
++      if (opts.histogram_on)
++          write_histogram (opts.Histogram_File_Name);
++    }
++
++    if( frame_stat[frame].status < PVM_FRAME_DONE_SHELLOUT) {
++      /* the shellouts are done in the correct order */
++      if( frame == next_frame_finish) {
++          int Frame_Result;
++
++          frame_stat[frame].status = PVM_FRAME_DONE_SHELLOUT;
++
++          opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame + frame;
++          setup_output_file_name();
++          
++          Frame_Result = POV_SHELLOUT(POST_FRAME_SHL);
++          
++          if ((Frame_Result==SKIP_ONCE_RET) || (Frame_Result==ALL_SKIP_RET)) {
++              next_frame_finish = frames;
++              return;
++          }
++          next_frame_finish ++;       /* one more frame finished */
++          
++          frame ++;           /* check next frame (might have finished earlier) */
++          if( frame < frames)
++              PvmFinishFrame();
++      }
++    }
++    if(opts.Options & VERBOSE)
++      Status_Info("\n");      
++}
++
++
++/*
++ * Assign Work
++ *
++ * A slaved requested work, now find free block (or a block to reassign)
++ */
++static void PvmAssignWork() {
++    int min_assigned;
++    int found;
++    int f;
++    
++    /* The slave wants work, but has already been reassigned something,
++     * then tell it to ask again when it really wants work.
++     */
++    if( slave_stat[slave_num].block_reassigned != PVM_SLAVE_FREE) {
++#ifdef DEBUG
++      Status_Info("%s already assigned on %d.  Not reassigning.\n",
++                  slave_name,
++                  slave_stat[slave_num].block_reassigned);
++#endif /* DEBUG */
++      pvm_initsend(PvmDataDefault);
++      pvm_send(rtid, PVM_INIT_SLAVE);
++      
++      return;
++    }
++
++
++    /*
++     * search for a free block, take a frame with minimal
++     * number of assigned blocks
++     */
++    found = 0;
++    min_assigned = grid_points;
++
++    frame = slave_stat[slave_num].frame_assigned;
++    /* if slave was already assigned and that frame has still free blocks */
++    if ( (frame >= 0) &&
++       (frame_stat[frame].block_to_assign < grid_points) ) {
++      
++      min_assigned = 0;
++      found = 1;
++    }
++    
++    /* find free frame with minimal number of assigned frames */
++    for(f = 0; (f < frames) && (min_assigned > 0); f ++) {
++      if( (frame_stat[f].block_to_assign < min_assigned) &&
++          (frame_stat[f].block_to_assign < grid_points) ) {
++          
++#ifdef DEBUG
++          Status_Info("\nfound: frame %d, ass: %d, min: %d\n", 
++                      f, frame_stat[f].block_to_assign,
++                      min_assigned);
++#endif
++
++          min_assigned = frame_stat[f].block_to_assign;
++          frame = f;
++          found = 1;
++
++      }
++    }
++
++    if( found ) {
++      /* first block for that frame */
++      if( frame_stat[frame].block_to_assign == 0) {   
++          /* Do the necessary preparations for a new frame */
++          if( PvmStartFrame() ) {
++              /* Shellout gave SKIP_ONCE_REt -> search next block */
++              PvmAssignWork();
++              return;
++          }
++      }
++
++      slave_stat[slave_num].frame_assigned = frame;
++      PvmSendWork(frame_stat[frame].block_to_assign);
++      return;
++    }
++
++
++    /*
++     * found no free block
++     * search for a block to reassign
++     */
++
++    /* if this slave was slow, and there are enough other slaves left,
++       don't use this anymore */
++    if ( (slave_stat[slave_num].pixels_done < npixels_done*9/10/PvmTasks) &&
++       (minions >= (PvmTasks + 3) / 4) ) {
++    
++      Status_Info("Not using %s for reassignment (%d%%)\n", slave_name,
++                  100*slave_stat[slave_num].pixels_done*PvmTasks/npixels_done);
++
++      /* tell slave to finish work */
++      pvm_initsend(PvmDataDefault);
++      pvm_send(rtid, PVM_STOP_SLAVE);
++    }
++
++    found = 0;
++
++    frame = slave_stat[slave_num].frame_assigned;
++    /* if slave was already assigned and that frame has free blocks */
++    if ( (frame >= 0) &&
++       (frame_stat[frame].block_to_reassign < grid_points) ) {
++      
++      min_assigned = 0;
++      found = 1;
++    }
++      
++    /* find first frame with minimal number of reassigned frames */
++    min_assigned = grid_points + 1;
++    for(f = 0; (f < frames) && (min_assigned > 0); f ++) {
++      if( frame_stat[f].block_to_reassign < min_assigned) {
++          
++          min_assigned = frame_stat[frame].block_to_reassign;
++          frame = f;
++          found = 1;
++      }
++    }
++    
++    if( found ) {
++      /* find block in frame */
++      int block;
++      for(block = 0; block < grid_points; block ++) {
++          if( frame_stat[frame].block_stat[block].status < PVM_GRID_POINT_DONE) {
++              slave_stat[slave_num].frame_reassigned = frame;
++              PvmReassignWork(block);
++              return;
++          }
++      }
++    }
++
++    /* 
++     * there is nothing left to reassign
++     */
++    Status_Info("All blocks are assigned. Stopping %s.\n", slave_name);
++
++    /* tell slave to finish work */
++    pvm_initsend(PvmDataDefault);
++    pvm_send(rtid, PVM_STOP_SLAVE);
++
++}
++
++/*
++ * display a part from one line
++ */
++static void PvmDisplayPlot(row_ptr, row, col, ncols) 
++    COLOUR * row_ptr;
++    int row, col, ncols;
++{
++    unsigned char Red, Green, Blue, Alpha;
++    DBL grey;
++    int i;
++    
++    /* draw line */
++    for (i = col; i < col+ncols; i++) {
++      extract_colors(row_ptr[i], &Red, &Green, &Blue, &Alpha, &grey);
++      POV_DISPLAY_PLOT(i, row, Red, Green, Blue, Alpha);
++    }
++    /* force draw of the whole line (with X11) */
++    i = opts.Last_Column - 1;
++    extract_colors(row_ptr[i], &Red, &Green, &Blue, &Alpha, &grey);
++    POV_DISPLAY_PLOT(i, row, Red, Green, Blue, Alpha);
++}
++
++
++/*
++ * one of the slaves sends a part of it's computed data
++ */
++static void PvmReceiveData() {
++    int ncols;
++    int i;
++    int block;
++    
++    pvm_upkint(&frame, 1, 1);
++    pvm_upkint(&block, 1, 1);
++    pvm_upkint(&row_start, 1, 1);
++    pvm_upkint(&col_start, 1, 1);
++    pvm_upkint(&ncols, 1, 1);
++
++    row_end = row_start + (bytesin - 3*sizeof(int))/(5*ncols*sizeof(COLC));
++    col_end = col_start + ncols;
++
++#ifdef DEBUG
++    Status_Info("receiving frame: %d, row: %d-%d, col: %d-%d\n", 
++              frame, row_start, row_end, col_start, col_end);
++#endif
++
++    /* Make some checks to see if the data is not corrupted or invalid */
++    if( (row_start < opts.First_Line) || (col_start < opts.First_Column) ||
++      (row_end   > opts.Last_Line)  || (col_end   > opts.Last_Column) ||
++      (frame     < 0)               || (frame     >= frames) ||
++      (ncols     > PvmChunkWidth)   || (row_end - row_start > PvmChunkHeight) ) {
++      
++      Warning(0.0, "Bad block recieved from %s\n",slave_name);
++      Warning(0.0, "Frame %d, Row %d - %d, Column %d - %d.\n", frame, row_start, row_end,
++              col_start, col_end);
++      
++      return;
++    }
++    
++    /* get one row after the other */
++    for (i = row_start; i < row_end; i++) {
++      if (i >= frame_stat[frame].block_stat[block].wr_line) {  /* Is incoming data current? */
++
++          /* allocate memory, to receive data */
++          if (frame_stat[frame].row_ptr[i] == NULL) { /* Starting a new row */
++              
++              frame_stat[frame].row_ptr[i] = (COLOUR *)POV_MALLOC(opts.Last_Column*sizeof(COLOUR), 
++                                                                  "row_ptr");
++              
++              while(frame_stat[frame].row_ptr[frame_stat[frame].row_to_malloc] != NULL && 
++                    frame_stat[frame].row_to_malloc < opts.Last_Line) {
++                  frame_stat[frame].row_to_malloc++;
++              }
++          }
++          
++          /* get color data */
++          pvm_upkCOL(&(frame_stat[frame].row_ptr[i][col_start][0]),5*ncols,1);
++          
++          frame_stat[frame].block_stat[block].wr_line = i + 1;
++
++          if (frame == frame_display) {
++              PvmDisplayPlot(frame_stat[frame].row_ptr[i], i, col_start, ncols);
++          }
++
++          /* Statistics - Attribute this grid section  */
++          grid_lines_done++;
++          slave_stat[slave_num].pixels_done += ncols;
++
++          npixels_done += ncols;
++      } else {  
++          /* another slave already sent this data */
++
++          COLOUR *dummy = (COLOUR *)POV_MALLOC(ncols * sizeof(COLOUR), "dummy row");
++          pvm_upkCOL(&(dummy[0][0]),5*ncols,1);
++          
++          /* Statistics - Attribute this grid section  */
++          slave_stat[slave_num].pixels_late += ncols;
++          POV_FREE(dummy);
++      }
++    }
++    /* Has this entire block been rendered? */
++    if (frame_stat[frame].block_stat[block].wr_line >= 
++      (block/grid_ppline + 1)*PvmChunkHeight + opts.First_Line - 1 ||
++      frame_stat[frame].block_stat[block].wr_line >= opts.Last_Line)  {
++#ifdef DEBUG
++      Status_Info("Block completed by %s\n", slave_name);
++#endif /* DEBUG */
++      frame_stat[frame].block_stat[block].status = PVM_GRID_POINT_DONE;
++      if (slave_stat[slave_num].block_reassigned == block) {
++#ifdef DEBUG
++              Status_Info("   reassigned block cleared.\n");
++#endif /* DEBUG */
++              slave_stat[slave_num].block_reassigned = PVM_SLAVE_FREE;
++      }
++#ifdef DEBUG
++      else
++      {
++          Status_Info("   reassigned block is %d.\n",
++                      slave_stat[slave_num].block_reassigned);
++      }
++#endif /* DEBUG */
++    }
++    
++    if ((opts.Options & VERBOSE) && (grid_lines_done % PvmChunkHeight) == 0) {
++      Status_Info("\r%5.2f of blocks complete.",
++                  (float)100*grid_lines_done/PvmChunkHeight/grid_points/frames);
++    }
++}
++
++/*
++ * Write out us many lines as possible 
++ */
++static void PvmWrite() {
++    int i;
++
++    do { 
++      /* check, if there is a complete line to write */
++      for (i = frame_stat[frame].block_to_write; 
++           i < frame_stat[frame].block_to_write + grid_ppline; i++)   {
++          if (frame_stat[frame].block_stat[i].wr_line <= frame_stat[frame].row_to_write) {
++#ifdef DEBUG
++              Status_Info("needing block %d in frame %d\n", i, frame); 
++#endif
++              return;
++          }
++      }
++
++      /* OK, we can write a whole line */
++      if (opts.Options & DISKWRITE) {
++#ifdef DEBUG
++          Status_Info("write: %d %d\n", frame, frame_stat[frame].row_to_write);
++#endif
++          Write_Line(frame_stat[frame].output_file,
++                     frame_stat[frame].row_ptr[frame_stat[frame].row_to_write],
++                     frame_stat[frame].row_to_write);
++      }
++
++      /* Still lines to allocate so */
++      if (frame_stat[frame].row_to_malloc < opts.Last_Line)  {
++          /* move this row buffer down, so we avaoid one malloc/free  */
++          frame_stat[frame].row_ptr[frame_stat[frame].row_to_malloc] = 
++              frame_stat[frame].row_ptr[frame_stat[frame].row_to_write];
++          frame_stat[frame].row_ptr[frame_stat[frame].row_to_write] = NULL;
++          frame_stat[frame].row_to_malloc++;
++      } else {                        /* Otherwise free the row buffer */
++          POV_FREE(frame_stat[frame].row_ptr[frame_stat[frame].row_to_write]);
++          frame_stat[frame].row_ptr[frame_stat[frame].row_to_write]= NULL;
++      }
++      
++      frame_stat[frame].row_to_write++;
++      
++#ifndef DEBUG
++      if ((opts.Options & VERBOSE) && (frame_stat[frame].row_to_write % 8) == 0) {
++          Status_Info("\r%5.2f of blocks complete.",
++                      (float)100*grid_lines_done/PvmChunkHeight/grid_points/frames);
++          Status_Info("  %4d of %4d lines finished (in frame %d).",
++                      frame_stat[frame].row_to_write - opts.First_Line, 
++                      opts.Last_Line - opts.First_Line, frame);
++      }       
++#endif /* not DEBUG */
++      
++      /* Finished a whole row  of blocks */
++      if ((frame_stat[frame].row_to_write - opts.First_Line) % PvmChunkHeight == 0) {
++          for (i = frame_stat[frame].block_to_write; 
++               i < frame_stat[frame].block_to_write + grid_ppline; i++) {
++              frame_stat[frame].block_stat[i].status = PVM_GRID_POINT_WRITTEN;
++          }
++          frame_stat[frame].block_to_write += grid_ppline;
++      }
++      
++    } while(frame_stat[frame].block_to_write < grid_points);
++}
++
++/*
++ * Handle slave requests, distribute and collect work
++ */
++void Pvm_Master_Control() {
++
++    /* Wait five minutes at a time between messages */
++    wait_time.tv_sec = 300;
++
++    next_frame_finish = 0;            /* next frame that finishes */
++
++    /* There are incomplete frames and/or incoming messages, and there are any slaves left*/
++    while ( (next_frame_finish < frames) || 
++          ((pvm_probe(-1, -1) > 0) && (minions > 0))) {
++
++      /* check for user abort */
++      TEST_ABORT;
++      if (Stop_Flag)  {
++          Render_Info("\nAborting render...\n");
++          
++          PvmMasterEnd();
++
++          if ((opts.Options & DISPLAY) && Display_Started) {
++              POV_DISPLAY_CLOSE;
++          }       
++          if (opts.Do_Stats) {
++              PRINT_STATS(stats);
++          }
++          Error("User abort.\n");
++
++          return;
++      }
++
++      /* receive a request from a slave */
++      if(PvmMasterReceive())
++          continue;
++
++      PvmIdentifySlave();
++
++#ifdef DEBUG
++      Status_Info("Received msg %d from %s\n", msgtag, slave_name);
++#endif
++
++      /* a slaves requests work */
++      switch(msgtag) {
++      case PVM_INIT_SLAVE:            /* Another slave started ok */
++          if (opts.Options & VERBOSE) {
++              Status_Info(" Slave %d at %s successfully started.\n", minions, slave_name);
++          }
++          minions++;
++          break;
++
++      case PVM_NEED_WORK:             /* a slave wants work */
++          PvmAssignWork();
++          break;
++          
++      case PVM_SLAVE_RESULTS:         /* slaves sends us some data */
++          PvmReceiveData();
++          PvmWrite();
++          PvmFinishFrame();
++          break;
++          
++      case PVM_SLAVE_STATS:           /* Too many stats, eh? */
++#ifdef DEBUG
++          Status_Info("Getting stats from %s.\n", slave_name);
++#endif /* DEBUG */
++          PvmUnpackStats();
++          nstat++;
++          break;
++
++      case PVM_SLAVE_EXIT:            /* Slave has exited. */
++          Status_Info("\nSlave at %s has exited.\n",slave_name);
++          
++          if (--minions <= 0)  {
++              Error_Line( "Error - All slave tasks have exited!\n");
++              pvm_exit();
++              return;
++          }
++          break;
++
++      default:                        /* something strange happend */
++          Warning(0.0, "Bad block recieved from %s - message type %4X.\n",
++                      slave_name, msgtag);
++      }
++    } /* incomplete frames */
++
++    /* Finished calculating everything */
++    PvmMasterEnd();
++}
++
++/*
++ * Stop PvmMaster
++ */
++static void PvmMasterEnd() {
++    int i;
++
++    /* tell all slave to stop work */
++    for(i=0; i < PvmTasks; i++) {
++      pvm_initsend(PvmDataDefault);
++      pvm_send(tids[i], PVM_STOP_SLAVE);
++    }
++
++    /* Wait a few seconds for lingering stats */
++    wait_time.tv_sec  = 4;
++    wait_time.tv_usec = 0;
++
++    if (opts.Options & VERBOSE)
++      Status_Info("\nWaiting for remaining slave stats.\n");
++
++    /* some incoming stats */
++    while ((bufid = pvm_trecv(-1, PVM_SLAVE_STATS, &wait_time)) > 0)  {
++#ifdef DEBUG
++      int msgtag;
++      if (pvm_bufinfo(bufid, &bytesin, &msgtag, &rtid) < 0) {
++          Error_Line( "PVM Master cannot bufinfo.\n");
++          pvm_perror("");
++          pvm_exit();
++          return;
++      }
++
++      PvmIdentifySlave();
++
++      Status_Info("Getting stats from %s.\n", slave_name);
++#endif /* DEBUG */
++
++      PvmUnpackStats();
++      nstat++;
++    }
++
++    /* Dump stats for the tasks */
++    Statistics( "\n\nPVM Task Distribution Statistics:\n");
++    Statistics( "%20s  [ done ] [ late ]","host name");
++    Statistics( "%20s  [ done ] [ late ]\n","host name");
++
++    for (i = 0; i < PvmTasks; i++) {
++      int dtid;
++      int j;
++
++      dtid = pvm_tidtohost(tids[i]);
++
++      for (j = 0; j < PvmTasks; j++)  {
++          if (dtid == hostinfo[j].hi_tid)
++              break;
++      }
++
++      Statistics("%20s  [%5.2f%%] [%5.2f%%]", hostinfo[j].hi_name,
++                 (DBL)slave_stat[i].pixels_done * 100 / npixels_done / frames,
++                 (DBL)slave_stat[i].pixels_late * 100 / npixels_done / frames);
++
++      if (i % 2 == 1)
++          Statistics("\n");
++    }
++
++    pvm_exit();
++
++    Statistics("\n\nPOV-Ray statistics for finished frames:");
++    if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME) {
++      for(i=0; i < next_frame_finish; i++) {
++          Statistics("\nFrame %d:\n", opts.FrameSeq.InitialFrame + i);
++          PRINT_STATS(frame_stat[i].stats);
++      }
++      if (opts.Do_Stats) {
++          Statistics("\n\nTotal statistics:");
++      }
++    }
++
++}
++
++/*
++ * A slave sent some stats
++ */
++static void PvmUnpackStats() {
++    COUNTER tmpStat[MaxStat];
++
++    pvm_upkint(&frame, 1, 1);
++
++    if( pvm_upkSTAT((long*)tmpStat, MaxStat*sizeof(COUNTER)/sizeof(long), 1) >= 0) {
++      int i;
++      COUNTER tmp;
++
++      for(i=0; i < MaxStat; i++) {
++          /* stats per frame */
++          Add_Counter(tmp,tmpStat[i],frame_stat[frame].stats[i]);
++          frame_stat[frame].stats[i] = tmp;
++
++          /* total stats */
++          Add_Counter(tmp,tmpStat[i],totalstats[i]);
++          totalstats[i] = tmp;
++
++          if(opts.FrameSeq.FrameType != FT_MULTIPLE_FRAME) {
++              Add_Counter(tmp,tmpStat[i],stats[i]);
++              stats[i] = tmp;
++          }
++      }
++    } else {
++      Error_Line("Unpack Stats.\n");
++      pvm_perror("");
++    }
++}
++
++
++
++/********************************************************************************
++ * Slave Intialization
++ ********************************************************************************/
++
++int Pvm_Slave_Init() {
++    /* Get master TID */
++    PvmMTid = pvm_parent();
++
++    /* Have PVM notify us if the host POV process is killed
++     * or the local PVM daemon dies */
++    pvm_notify(PvmTaskExit, PVM_KILL_SLAVE, 1, &PvmMTid);
++    pvm_setopt(PvmAutoErr, 2);
++    
++    /* Set direct routing */
++    pvm_setopt(PvmRoute, PvmRouteDirect);
++    
++    /* Tell the master we started successfully */
++    pvm_initsend(PvmDataDefault);
++    pvm_send(PvmMTid, PVM_INIT_SLAVE);
++    
++    /* Send an initial work request to the master, so that there is always
++     * an outstanding block assignment, and we don't have to wait for the
++     * master to respond to our request.
++     */
++    pvm_initsend(PvmDataDefault);
++    pvm_send(PvmMTid, PVM_NEED_WORK);
++    
++    NICE(PvmNice);
++    
++    return 0;
++}
++
++
++/********************************************************************************
++ * Slave Control
++ ********************************************************************************/
++
++static void PvmSendStats() {
++    pvm_initsend(PvmDataDefault);
++    pvm_pkint( &frame, 1, 1);
++    pvm_pkSTAT( (long*)stats, MaxStat*sizeof(COUNTER)/sizeof(long), 1);
++    pvm_send(PvmMTid, PVM_SLAVE_STATS);
++}
++
++/*
++ * Request a new block from the master,
++ * handle all messages sent by the master
++ * ret: 0 .. continue with new frame (or exit)
++ *      1 .. continue with this frame (but new region)
++ */
++int Pvm_Slave_Control() {
++
++    struct timeval wait_time;
++    int bufid;
++    unsigned int msgtag;
++    int f;
++    static int first = 1;
++
++    /* Wait up to 5 minutes for a message, otherwise exit */
++    wait_time.tv_sec  = 300;
++    wait_time.tv_usec = 0;
++
++    if((bufid = pvm_trecv(PvmMTid, -1, &wait_time)) == 0) {
++      Error_Line( "Slave timed out waiting for master\n");
++      pvm_exit();
++      exit(1);
++    } else if (bufid < 0) {
++      Error_Line( "Cannot receive message from master.");
++      pvm_perror("");
++      pvm_exit();
++      exit(1);
++    }
++
++    /* We have a block assignment from the master - IGOR SERVES! */
++    if(pvm_bufinfo(bufid, (int *)0, &msgtag, &PvmMTid) < 0) {
++      Error_Line("Cannot get PVM bufinfo\n");
++      pvm_perror("");
++      pvm_exit();
++      exit(1);
++    }
++    
++    switch( msgtag) {
++    case PVM_INIT_SLAVE:
++      pvm_initsend(PvmDataDefault);
++      pvm_send(PvmMTid, PVM_NEED_WORK);
++      break;
++
++    case PVM_WORK:
++      /* Ask master for some more block assignments in advance
++       * to avoid waiting for them to be sent later
++       */
++      pvm_initsend(PvmDataDefault);
++      pvm_send(PvmMTid, PVM_NEED_WORK);
++
++      /* Unpack the message */
++      pvm_upkint(&f, 1, 1);
++      pvm_upkint(&PvmBlockNum, 1, 1);
++      pvm_upkint(&opts.First_Line, 1, 1);
++      pvm_upkint(&opts.Last_Line, 1, 1);
++      pvm_upkint(&opts.First_Column, 1, 1);
++      pvm_upkint(&opts.Last_Column, 1, 1);
++
++      /* Last_Line is really "number of lines to do" */
++      opts.Last_Line++;
++
++      /* Send the data every 1/4 of a block */
++      PvmRowsToSend = (opts.Last_Line - opts.First_Line + 3) / 4;
++
++      /* Last_Column is really "number of columns to do" */
++      opts.Last_Column++;
++      
++      /* check, if a new frame starts */
++      if(frame != f) {
++          if(!first)
++              PvmSendStats();
++          first = 0;
++          PvmNextFrame = opts.FrameSeq.InitialFrame + f;
++          frame = f;
++          return 0;           /* stop with this frame */
++      }
++      
++      /* We need to do this for every block */
++      Initialize_Renderer();
++      
++      return 1;               /* still the same frame */
++
++    case PVM_STOP_SLAVE:      /* Everything is done, send stats and exit */
++      PvmSendStats();
++      PvmNextFrame = opts.FrameSeq.FinalFrame + 1;
++      return 0;
++
++    case PVM_KILL_SLAVE:
++      Error_Line("\nSlave killed because of dead master.\n");
++      pvm_exit();
++      exit(1);
++
++    default: /* Unknown message */
++      Error_Line("\nUnknown control message from master - %X\n",msgtag);
++      pvm_initsend(PvmDataDefault);
++      pvm_send(PvmMTid,PVM_NEED_WORK);  /* Request work again */
++    }
++    return 0;
++}
++
++void Pvm_Slave_Exit() {
++    pvm_initsend(PvmDataDefault);
++    pvm_send(PvmMTid, PVM_SLAVE_EXIT);
++    pvm_exit();
++}
++
++void Pvm_Write_Line(handle, line_data, line_number)
++    FILE_HANDLE     *handle;
++    COLOUR          *line_data;
++    int             line_number;
++{
++    static int in_buffer = 0;
++    static int ncols;
++
++    if (in_buffer == 0) /* No lines written yet. Initialize buffer */
++    {
++      ncols = opts.Last_Column - opts.First_Column;
++      pvm_initsend(PvmDataDefault);
++
++      /* Insert block info into message */
++      pvm_pkint(&frame, 1, 1);
++      pvm_pkint(&PvmBlockNum, 1, 1);
++      pvm_pkint(&line_number, 1, 1);
++      pvm_pkint(&opts.First_Column, 1, 1);
++      pvm_pkint(&ncols, 1, 1);
++    }
++
++    if (pvm_pkCOL(&(line_data[opts.First_Column][0]),5*ncols, 1) < 0) {
++      Error_Line( "Cannot pack in Pvm_Write_Line\n");
++      pvm_perror("");
++      if (in_buffer > 0) {
++          pvm_send(PvmMTid, PvmBlockNum);  /* Send any lines we have done. */
++      }
++      pvm_initsend(PvmDataDefault);
++      pvm_send(PvmMTid, PVM_SLAVE_EXIT); /* Tell the master we're dying */
++      pvm_exit();
++      return;
++    }
++
++    in_buffer++;
++    
++    if (in_buffer >= PvmRowsToSend || line_number >= opts.Last_Line - 1) {
++      if (pvm_send(PvmMTid, PVM_SLAVE_RESULTS) < 0) { 
++          Error_Line( "Cannot send block in Pvm_Write_Line\n");
++          pvm_perror("");
++          pvm_exit();
++          return;
++      }
++
++      in_buffer = 0;
++    }
++} 
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
+diff -Naur source.ori/pvm.h source/pvm.h
+--- source.ori/pvm.h   Thu Jan  1 01:00:00 1970
++++ source/pvm.h       Sun Sep 12 00:25:35 1999
+@@ -0,0 +1,164 @@
++/****************************************************************************
++*  pvm.h
++*
++*  This include module is used by PVM control routines in the PVM'd PovRAY.
++*
++*  This file was written by Brad Kline.  April 1994.
++*                modified by Andreas Dilger,  Feb 1995.
++*                modified by Harald Deischinger, 1996 - April 1997
++*                modified by Jakob Flierl, May 1999
++* 
++*  All the defines that need modification are at the top.
++*
++*  The author disclaims all warranties with regard to this software, 
++*  including all implied warranties of merchant-ability and fitness. 
++*  The code is simply distributed as it is.
++*
++*
++*****************************************************************************/
++
++/* If setpriority() doesn't exist on your system, you should change 
++ * "your_pvm_arch" on the following line to whatever your actual PVM_ARCH
++ * is (ie ALPHA or SCO), and please email me to tell me about it, so I can
++ * add it in permanently.
++ */
++
++#include <features.h>
++
++#if defined(SUN4SOL2) || defined(your_pvm_arch)
++
++#define NICE(x) nice(x)
++extern int nice PARAMS((int prio));
++
++#else
++
++/* This should work for the majority of systems */
++
++#define NICE(x) setpriority(PRIO_PROCESS,0,getpriority(PRIO_PROCESS,0)+(x))
++
++#if !(defined __GLIBC__ && __GLIBC__ >= 2)
++extern int setpriority PARAMS((int which, int who, int prio));
++extern int getpriority PARAMS((int which, int who));
++#endif
++#endif
++
++/* How nice to be by default. 0 is the regular job priority, while is 20 very
++ * nice.  The nice value can be changed on the command line, so there is
++ * probably little reason to change it here.
++ */
++#define PVM_DEFAULT_NICE 5
++
++/* On BSD systems, use getwd instead of getcwd.  Change "your_pvm_arch" on
++ * the following line to whatever your actual PVM_ARCH is (ie MIPS or CRAY),
++ * and please email me to tell me about it, so I can add it in permanently.
++ */
++#if defined(NEXT) || defined(your_pvm_arch)
++
++#define GETCWD(path) getwd(path)
++extern char *getwd PARAMS((char *));
++
++#else
++
++#define GETCWD(path) getcwd(path,PATH_MAX+1)
++extern char *getcwd PARAMS((char *, size_t ));
++
++#endif
++
++extern int  chdir PARAMS((const char *path));
++
++/* Grid decomposition defaults */
++#define PVM_DEFAULT_GRID_HEIGHT     32
++#define PVM_DEFAULT_GRID_WIDTH      32
++
++/* what type of encoding to use whan sending pixels */
++#define pvm_upkCOL pvm_upkfloat
++#define pvm_pkCOL pvm_pkfloat
++#define pvm_upkSTAT pvm_upklong
++#define pvm_pkSTAT pvm_pklong
++
++
++/* The maximum path length. _POSIX_PATH_MAX_ = 255 in my limits.h file, but
++ * not all systems have this defined, and my base SUN4 install didn't have
++ * PATH_MAX in <limits.h>.*/
++#ifndef PATH_MAX
++#ifdef _POSIX_PATH_MAX
++#define PATH_MAX _POSIX_PATH_MAX
++#else  /* _POSIX_PATH_MAX doesn't exist.  A safe guess. */
++#define PATH_MAX 1024
++#endif  /* _POSIX_PATH_MAX */ 
++#endif  /* PATH_MAX */ 
++
++/* Structure to hold info about each slave process */
++typedef struct {
++    char *name;
++    int pixels_done;          /* Number of pixels completed */
++    int pixels_late;          /* Number of pixels finished after another slave did it */
++    int block_reassigned;     /* Block number reassigned to this slave */
++    int frame_assigned;               /* assigned to that frame */
++    int frame_reassigned;     /* reassigned to that frame */
++} pvm_slave_stat;
++#define PVM_SLAVE_FREE           -1
++
++/* Structure to hold info about each grid section */
++typedef struct {
++    int status;          /* Current status of block */
++    int wr_line;         /* Line to be written next */
++    int assigned_tid;    /* Tid of the machine assigned to this block */
++    int reassigned_tid;  /* Tid of the machine reassigned to this block */
++} pvm_block_stat;
++/* Status indicators */
++#define PVM_GRID_POINT_FREE       0
++#define PVM_GRID_POINT_ASSIGNED   1
++#define PVM_GRID_POINT_REASSIGNED 2
++#define PVM_GRID_POINT_DONE       3
++#define PVM_GRID_POINT_WRITTEN    4
++
++
++/* Structure to hold info about each frame */
++typedef struct {
++    int status;
++    COLOUR ** row_ptr;
++    int row_to_write;
++    int row_to_malloc;
++    int block_to_assign;
++    int block_to_reassign;
++    int block_to_write;
++    COUNTER stats[MaxStat];           /* PovRay Status */
++    pvm_block_stat * block_stat;
++    FILE_HANDLE* output_file;
++} pvm_frame_stat;
++#define PVM_FRAME_FREE                0
++#define PVM_FRAME_WORKING     1
++#define PVM_FRAME_DONE                2
++#define PVM_FRAME_DONE_SHELLOUT               3
++
++/* Define some message IDs to be used */
++#define PVM_INIT_SLAVE  1
++#define PVM_STOP_SLAVE  2
++#define PVM_KILL_SLAVE  3
++#define PVM_NEED_WORK   4
++#define PVM_SLAVE_EXIT  5
++#define PVM_SLAVE_STATS 6
++#define PVM_SLAVE_RESULTS 7
++#define PVM_WORK      8
++
++/* Prototypes for public functions defined in pvm.c */
++void Pvm_Master_Init PARAMS((int argc, char **argv));
++void Pvm_Master_Control PARAMS((void));
++void Pvm_Write_Line PARAMS((FILE_HANDLE *handle, COLOUR *line, int row));
++int  Pvm_Slave_Init PARAMS((void));
++int  Pvm_Slave_Control PARAMS((void));
++void Pvm_Slave_Exit PARAMS((void));
++
++extern char     PvmArch[];
++extern int      PvmChunkWidth;
++extern int      PvmChunkHeight;
++extern int      PvmSlave;
++extern int      PvmTasks;
++extern int      PvmNice;
++extern char * PvmSlavename;
++extern int    PvmNextFrame;
++extern int    PvmBlockNum;
++extern char   PvmWorkingDir[];
++extern char **  PvmHosts;
++extern int    PvmHostsN;
+diff -Naur source.ori/render.c source/render.c
+--- source.ori/render.c        Sat Jun 30 01:49:21 2001
++++ source/render.c    Sat Jun 30 10:08:01 2001
+@@ -46,7 +46,9 @@
+ #include "texture.h"
+ #include "vbuffer.h"
+ #include "userio.h"
+-
++#ifdef USE_PVM
++#include "pvm.h"
++#endif
+ /*****************************************************************************
+ * Local preprocessor defines
+@@ -65,6 +67,10 @@
+ #define SUB_PIXEL_GRID_SIZE 16
++#ifdef USE_PVM
++DBL maxclr;
++#endif
++
+ /*****************************************************************************
+ * Local typedefs
+ ******************************************************************************/
+@@ -104,7 +110,9 @@
+ static int SuperSampleCount, RadiosityCount;
++#ifndef USE_PVM
+ static DBL maxclr;
++#endif
+ /* Jitter values are taken from [-0.5*JitterScale, 0.5*JitterScale]. */
+@@ -314,7 +322,9 @@
+ static int  create_ray (RAY *ray, DBL x, DBL y, int ray_number);
+ static void supersample (COLOUR result, int x, int y);
+ static void gamma_correct (COLOUR Colour);
++#ifndef USE_PVM
+ static void extract_colors (COLOUR Colour, unsigned char *Red, unsigned char *Green, unsigned char *Blue, unsigned char *Alpha, DBL *grey);
++#endif
+ static void trace_pixel (int x, int y, COLOUR Colour);
+ static void initialise_histogram (void) ;
+ static void accumulate_histogram (int x, int y, int on);
+@@ -349,7 +359,8 @@
+ *
+ * CHANGES
+ *
+-*   -
++*   Oct 1996: Added support for PVM [Deischi]
++*   Jun 2001: Added conditional build for PVM Support [Agaran]
+ *
+ ******************************************************************************/
+@@ -368,6 +379,12 @@
+   size = (Frame.Screen_Width + 1) * sizeof(COLOUR);
++#ifdef USE_PVM
++  /*  looks like normal mem leak,  maybe that should be always enabled? */
++  if (Previous_Line!=NULL) POV_FREE(Previous_Line);
++  if (Current_Line!=NULL) POV_FREE(Current_Line);
++#endif
++
+   Previous_Line = (COLOUR *)POV_MALLOC(size, "previous line buffer");
+   Current_Line  = (COLOUR *)POV_MALLOC(size, "current line buffer");
+@@ -381,6 +398,12 @@
+   {
+     size = (Frame.Screen_Width + 1) * sizeof(char);
++#ifdef USE_PVM
++    /* again looks like memleak fixup... */
++    if (Previous_Line_Antialiased_Flags!=NULL) POV_FREE(Previous_Line_Antialiased_Flags);
++    if (Current_Line_Antialiased_Flags!=NULL) POV_FREE(Current_Line_Antialiased_Flags);
++#endif
++    
+     Previous_Line_Antialiased_Flags = (char *)POV_MALLOC(size, "previous line flags");
+     Current_Line_Antialiased_Flags  = (char *)POV_MALLOC(size, "current line flags");
+@@ -390,6 +413,11 @@
+       Current_Line_Antialiased_Flags[i]  = 0;
+     }
+   }
++#ifdef USE_PVM
++  /* If PVM, this is already enough */
++  if(PvmTasks && !PvmSlave)
++      return;
++#endif
+   Assign_Vector(Camera_Ray.Initial, Frame.Camera->Location);
+@@ -3154,8 +3182,11 @@
+ *   Jun 1995 : Alpha channel support -CEY
+ *
+ ******************************************************************************/
+-
++#ifdef USE_PVM
++void extract_colors(COLOUR Colour, unsigned char *Red, unsigned char  *Green, unsigned char  *Blue, unsigned char  *Alpha, DBL *grey)
++#else
+ static void extract_colors(COLOUR Colour, unsigned char *Red, unsigned char  *Green, unsigned char  *Blue, unsigned char  *Alpha, DBL *grey)
++#endif
+ {
+   if (opts.PaletteOption == GREY)
+   {
+diff -Naur source.ori/render.h source/render.h
+--- source.ori/render.h        Sat Jun 30 01:49:21 2001
++++ source/render.h    Sat Jun 30 03:18:56 2001
+@@ -59,6 +59,9 @@
+ extern unsigned long              *histogram_grid ;
+ extern unsigned long              max_histogram_value ;
+ extern FILE_HANDLE                *Histogram_File_Handle ;
++#ifdef USE_PVM
++extern DBL                        maxclr;
++#endif
+ /*****************************************************************************
+ * Global functions
+@@ -74,6 +77,10 @@
+ DBL  Trace (RAY *Ray, COLOUR Colour, DBL Weight);
+ void Check_User_Abort (int Do_Stats);
+ void write_histogram (char *filename);
++#ifdef USE_PVM
++void extract_colors (COLOUR Colour, unsigned char *Red, unsigned char *Green, unsigned char *Blue, unsigned char *Alpha, DBL *grey);
++#endif
++
+ void destroy_histogram (void);
+ #endif
+diff -Naur source.ori/unix/makefile source/unix/makefile
+--- source.ori/unix/makefile   Sat Jun 30 03:02:03 2001
++++ source/unix/makefile       Sat Jun 30 11:54:57 2001
+@@ -53,7 +53,7 @@
+ #CFLAGS      = -O6 -finline-functions -ffast-math -c -ansi -m386 -DCPU=586 -DCOMPILER_VER=\".`uname`.$(CC)\" -DPOV_LIB_DIR=\"$(POVLIBDIR)\" $(SRCINC) $(LIBPNGINC) $(ZLIBINC)
+ # Linux compiler flags, Pentium II optimized
+-CFLAGS      = $(OPT_FLAGS) -finline-functions -ffast-math -c -ansi -DCOMPILER_VER=\".`uname`.$(CC)\" -DPOV_LIB_DIR=\"$(POVLIBDIR)\" $(SRCINC)
++CFLAGS      = $(OPT_FLAGS) $(PVMFLAGS) -finline-functions -ffast-math -c -ansi -DCOMPILER_VER=\".`uname`.$(CC)\" -DPOV_LIB_DIR=\"$(POVLIBDIR)\" $(SRCINC)
+ # HPUX compiler flags
+ #CFLAGS      = +O2 -finline-functions -c -Aa -D_HPUX_SOURCE -DCOMPILER_VER=\".`uname`.$(CC)\" $(SRCINC) $(LIBPNGINC) $(ZLIBINC)
+@@ -149,12 +149,15 @@
+ # UTARGET is the name of the text-only UNIX version
+ UTARGET=povray
++UTARGET_PVM=pvmpov
+ # XTARGET is the name of the X-Windows executable.
+ XTARGET=x-povray
++XTARGET_PVM=x-pvmpov
+ # STARGET is the name of the SVGA executable.
+ STARGET=s-povray
++STARGET_PVM=s-pvmpov
+ # This is the suffix for object files.
+ OBJ     = .o
+@@ -167,6 +170,13 @@
+ SRCINC  = -I. -I$(SRCDIR)
++# not needed
++#PVMINC               = -I$(PVM_ROOT)/include
++#PVMLIB               = /usr/lib/libpvm3.a /usr/lib/libgpvm3.a        
++PVMLIB                = -lpvm3
++# dunno for what that is used
++XDIR          = $(HOME)/pvm3/bin/$(PVM_ARCH) 
++
+ #
+ # End of user specific options
+ #
+@@ -1526,6 +1536,12 @@
+                           xpovmask.xbm         \
+                           $(SRCDIR)/povproto.h
++pvmDEP                  = $(SRCDIR)/pvm.c      \
++                          config.h             \
++                          $(SRCDIR)/pvm.h
++#                          $(UNIXDIR)/unixconf.h
++
++
+ config.h:
+       @echo
+       @echo 'You need to select which executable version you want'
+@@ -1549,46 +1565,89 @@
+ unix: $(UTARGET)
+       @echo
++unix_pvm:     $(UTARGET_PVM)
++      @echo
++
+ $(UTARGET):   $(POVOBJS) $(ODIR)/unix$(OBJ)
+       $(CC) $(POVOBJS) $(ODIR)/unix$(OBJ) $(LFLAGS) -o $(UTARGET)
++$(UTARGET_PVM):       $(POVOBJS) $(ODIR)/pvm$(OBJ) $(ODIR)/unix$(OBJ)
++      $(CC) $(POVOBJS) $(ODIR)/unix$(OBJ) $(ODIR)/pvm$(OBJ) $(PVMLIB) $(LFLAGS) -o $(UTARGET_PVM)
++
+ svga: $(STARGET)
+       @echo
++svga_pvm:     $(STARGET_PVM)
++      @echo
++
++
+ $(STARGET):   $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/unix$(OBJ)
+       $(CC) $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/unix$(OBJ) $(LFLAGS) $(SLIBLIB) -o $(STARGET)
++$(STARGET_PVM):       $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/pvm$(OBJ) $(ODIR)/unix$(OBJ)
++      $(CC) $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/unix$(OBJ) $(ODIR)/pvm$(OBJ) $(PVMLIB) $(LFLAGS) $(SLIBLIB) -o $(STARGET_PVM)
++
+ xwin: $(XTARGET)
+       @echo
++xwin_pvm:     $(XTARGET_PVM)
++      @echo
++
+ $(XTARGET):   $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/unix$(OBJ)
+       $(CC) $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/unix$(OBJ) $(LFLAGS) $(XLIBLIB) -o $(XTARGET)
++$(XTARGET_PVM):       $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/pvm$(OBJ) $(ODIR)/unix$(OBJ)
++      $(CC) $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/unix$(OBJ) $(ODIR)/pvm$(OBJ) $(PVMLIB) $(LFLAGS) $(XLIBLIB) -o $(XTARGET_PVM)
++
++
+ # Only these files need to be rebuilt if the display type was changed
+ DISPOBJS = $(ODIR)/povray$(OBJ) $(ODIR)/render$(OBJ) \
+            $(ODIR)/userio$(OBJ) $(ODIR)/vbuffer$(OBJ)
++PVMOBJ = $(ODIR)/optin$(OBJ) $(ODIR)/optout$(OBJ) $(ODIR)/povray$(OBJ) $(ODIR)render$(OBJ)
++
+ newunix:
+-      -@$(RM) $(DISPOBJS)
++      -@$(RM) $(DISPOBJS) $(PVMOBJ)
+       -@cp unixconf.h config.h
+       -@touch -c $(ODIR)/*$(OBJ)
+       -@$(MAKE) unix
++newunix_pvm:
++      -@$(RM) $(DISPOBJS) $(PVMOBJ)
++      -@cp unixconf.h config.h
++      -@touch -c $(ODIR)/*$(OBJ)
++      -@$(MAKE) PVMFLAGS="-DUSE_PVM" unix_pvm
++
++
+ newsvga:
+-      -@$(RM) $(DISPOBJS)
++      -@$(RM) $(DISPOBJS) $(PVMOBJ)
+       -@cp svgaconf.h config.h
+       -@touch -c $(ODIR)/*$(OBJ)
+       -@$(MAKE) svga
++newsvga_pvm:
++      -@$(RM) $(DISPOBJS) $(PVMOBJ)
++      -@cp svgaconf.h config.h
++      -@touch -c $(ODIR)/*$(OBJ)
++      -@$(MAKE) PVMFLAGS="-DUSE_PVM" svga_pvm
++
+ newxwin:
+-      -@$(RM) $(DISPOBJS)
++      -@$(RM) $(DISPOBJS) $(PVMOBJ)
+       -@cp xwinconf.h config.h
+       -@touch -c $(ODIR)/*$(OBJ)
+       -@$(MAKE) xwin
++newxwin_pvm:
++      -@$(RM) $(DISPOBJS) $(PVMOBJ)
++      -@cp xwinconf.h config.h
++      -@touch -c $(ODIR)/*$(OBJ)
++      -@$(MAKE) PVMFLAGS="-DUSE_PVM" xwin_pvm
++
++
+ clean:
+-      -@$(RM) $(POVOBJS) $(ODIR)/unix$(OBJ) $(ODIR)/xwindows$(OBJ)
+-      -@$(RM) $(ODIR)/svga$(OBJ) $(UTARGET) $(XTARGET) $(STARGET)
++      -@$(RM) $(POVOBJS) pvm$(OBJ) $(ODIR)/unix$(OBJ) $(ODIR)/xwindows$(OBJ)
++      -@$(RM) $(ODIR)/svga$(OBJ) $(UTARGET) $(XTARGET) $(STARGET) $(UTARGET_PVM) 
++      -@$(RM) $(XTARGET_PVM) $(STARGET_PVM) 
+ install:
+       -@cp povray.1 $(POVPATH)/man/man1
+@@ -1821,17 +1880,7 @@
+ $(ODIR)/xwindows$(OBJ) : $(xwindowsDEP)
+       $(CC) $(CFLAGS) $(XLIBINC) xwindows.c
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
++$(ODIR)/pvm$(OBJ) : $(pvmDEP)
++      $(CC) $(CFLAGS) $(SRCDIR)/pvm.c
This page took 0.404963 seconds and 4 git commands to generate.