/**************************************************************************** * povray.c * * This module contains the entry routine for the raytracer and the code to * parse the parameters on the command line. * * from Persistence of Vision(tm) Ray Tracer * Copyright 1996 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ #include #include /* BP */ #include "frame.h" /* common to ALL modules in this program */ #include "povproto.h" #include "bezier.h" #include "blob.h" #include "bbox.h" #include "cones.h" #include "csg.h" #include "discs.h" #include "express.h" #include "fractal.h" #include "hfield.h" #include "lathe.h" #include "lighting.h" #include "mem.h" #include "mesh.h" #include "polysolv.h" #include "objects.h" #include "octree.h" #include "parse.h" #include "pigment.h" #include "point.h" #include "poly.h" #include "polygon.h" #include "povray.h" #include "optin.h" #include "optout.h" #include "quadrics.h" #include "pgm.h" #include "png_pov.h" #include "ppm.h" #include "prism.h" #include "radiosit.h" #include "render.h" #include "sor.h" #include "spheres.h" #include "super.h" #include "targa.h" #include "texture.h" #include "tokenize.h" #include "torus.h" #include "triangle.h" #include "truetype.h" #include "userio.h" /*Error,Warning,Init_Text_Streams*/ #include "lbuffer.h" #include "vbuffer.h" /***************************************************************************** * Local preprocessor defines ******************************************************************************/ /* Flags for the variable store. */ #define STORE 1 #define RESTORE 2 /***************************************************************************** * Local typedefs ******************************************************************************/ /***************************************************************************** * Local variables ******************************************************************************/ /* The frame. */ FRAME Frame; /* Options and display stuff. */ char Color_Bits; int Display_Started; int Abort_Test_Every; int Experimental_Flag; /* Current stage of the program. */ int Stage; /* Flag if -h option will show help screens. */ int Help_Available; /* File and parsing stuff. */ Opts opts; COUNTER stats[MaxStat]; COUNTER totalstats[MaxStat]; int Num_Echo_Lines; /* May make user setable later - CEY*/ int Echo_Line_Length; /* May make user setable later - CEY*/ int Number_Of_Files; FILE *stat_file; FILE_HANDLE *Output_File_Handle; char Actual_Output_Name[FILE_NAME_LENGTH]; /* Timing stuff .*/ time_t tstart, tstop; DBL tparse, trender, tparse_total, trender_total; /* Variable used by vector macros. */ DBL VTemp; volatile int Stop_Flag; /* Flag if close_all() was already called. */ static int closed_flag; int pre_init_flag=0; char *Option_String_Ptr; /***************************************************************************** * Static functions ******************************************************************************/ static void init_vars PARAMS((void)); static void destroy_libraries PARAMS((void)); static void fix_up_rendering_window PARAMS((void)); static void fix_up_animation_values PARAMS((void)); static void fix_up_scene_name PARAMS((void)); static void set_output_file_handle PARAMS((void)); static void setup_output_file_name PARAMS((void)); static void open_output_file PARAMS((void)); static void FrameRender PARAMS((void)); static void init_statistics PARAMS((COUNTER *)); static void sum_statistics PARAMS((COUNTER *, COUNTER *)); static void variable_store PARAMS((int Flag)); static int Has_Extension PARAMS((char *name)); static unsigned closest_power_of_2 PARAMS((unsigned theNumber)); static void init_shellouts PARAMS((void)); static void destroy_shellouts PARAMS((void)); /***************************************************************************** * * FUNCTION * * main * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * - * * CHANGES * * - * ******************************************************************************/ #ifdef NOCMDLINE /* a main() by any other name... */ #ifdef ALTMAIN MAIN_RETURN_TYPE alt_main() #else MAIN_RETURN_TYPE main() #endif #else #ifdef ALTMAIN MAIN_RETURN_TYPE alt_main(argc, argv) #else MAIN_RETURN_TYPE main(argc, argv) #endif int argc; char **argv; #endif /* ...would be a lot less hassle!! :-) AAC */ { register int i; int Diff_Frame; DBL Diff_Clock; SHELLRET Pre_Scene_Result, Frame_Result; /* Attention all ALTMAIN people! See comments attached to this function*/ pre_init_povray(); /* Startup povray. */ Stage = STAGE_STARTUP; STARTUP_POVRAY /* Print banner and credit info. */ Stage = STAGE_BANNER; PRINT_CREDITS PRINT_OTHER_CREDITS #ifndef NOCMDLINE /* Print help screens. */ if (argc == 1) { Print_Help_Screens(); } #endif /* Initialize variables. */ init_vars(); Stage = STAGE_ENVIRONMENT; READ_ENV_VAR Stage = STAGE_INI_FILE; /* Read parameters from POVRAY.INI */ PROCESS_POVRAY_INI #ifndef NOCMDLINE /* Parse the command line parameters */ Stage = STAGE_COMMAND_LINE; Help_Available = (argc == 2); for (i = 1 ; i < argc ; i++ ) { parse_option_line(argv[i]); } #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(); /* Write .INI file [SCD 2/95] */ Write_INI_File(); ALT_WRITE_INI_FILE /* Make sure clock is okay, validate animation parameters */ fix_up_animation_values(); /* Fix-up rendering window values if necessary. */ fix_up_rendering_window(); /* Set output file handle for options screen. */ set_output_file_handle(); /* Print options used. */ Print_Options(); /* BEGIN SECTION */ /* VARIOUS INITIALIZATION THAT ONLY NEEDS TO BE DONE 1/EXECUTION */ /* Set up noise-tables. */ Initialize_Noise(); Diff_Clock = opts.FrameSeq.FinalClock - opts.FrameSeq.InitialClock; if (opts.Options & CYCLIC_ANIMATION) { Diff_Frame = opts.FrameSeq.FinalFrame - opts.FrameSeq.InitialFrame + 1; } else { Diff_Frame = opts.FrameSeq.FinalFrame - opts.FrameSeq.InitialFrame; } /* END SECTION */ /* Execute the first shell-out command */ Pre_Scene_Result=POV_SHELLOUT(PRE_SCENE_SHL); /* Loop over each frame */ if (Pre_Scene_Result != ALL_SKIP_RET) { if (Pre_Scene_Result != SKIP_ONCE_RET) { 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 += ((Diff_Frame==0)?1:Diff_Clock/Diff_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) { break; } if (Frame_Result != SKIP_ONCE_RET) { FrameRender(); /* Execute a shell-out command after tracing */ Frame_Result = POV_SHELLOUT(POST_FRAME_SHL); if ((Frame_Result==SKIP_ONCE_RET) || (Frame_Result==ALL_SKIP_RET)) { break; } } } /* Print total stats ... */ if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME) { Statistics("\nTotal Statistics"); opts.FrameSeq.FrameNumber--; PRINT_STATS(totalstats); opts.FrameSeq.FrameNumber++; } } /* Execute the final shell-out command */ POV_SHELLOUT(POST_SCENE_SHL); } /* And finish. */ Terminate_POV(0); MAIN_RETURN_STATEMENT } /* main */ /***************************************************************************** * * FUNCTION * * FrameRender * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Do all that is necessary for rendering a single frame, including parsing * * CHANGES * * Feb 1996: Make sure we are displaying when doing a mosaic preview [AED] * ******************************************************************************/ static void FrameRender() { unsigned long hours, minutes; DBL seconds, t_total; /* Store start time for parse. */ START_TIME /* Parse the scene file. */ Status_Info("\n\nParsing..."); opts.Do_Stats=FALSE; Init_Random_Generators(); Parse(); Destroy_Random_Generators(); opts.Do_Stats=TRUE; if (opts.Options & RADIOSITY) { Experimental_Flag |= EF_RADIOS; } if (Experimental_Flag) { Warning(0.0,"Warning: This rendering uses the following experimental features:\n"); if (Experimental_Flag & EF_RADIOS) { Warning(0.0," radiosity"); } if (Experimental_Flag & EF_HALOS) { Warning(0.0," halos"); } if (Experimental_Flag & EF_ATMOS) { Warning(0.0," atmosphere"); } Warning(0.0,".\nThe design and implementation of these features is likely to\n"); Warning(0.0,"change in future versions of POV-Ray. Full backward compatibility\n"); Warning(0.0,"with the current implementation is NOT guaranteed.\n"); } Experimental_Flag=0; /* Switch off standard anti-aliasing. */ if ((Frame.Camera->Aperture != 0.0) && (Frame.Camera->Blur_Samples > 0)) { opts.Options &= ~ANTIALIAS; Warning(0.0, "Focal blur is used. Standard antialiasing is switched off.\n"); } /* Create the bounding box hierarchy. */ Stage = STAGE_SLAB_BUILDING; if (opts.Use_Slabs) { Status_Info("\nCreating bounding slabs."); } /* Init module specific stuff. */ Initialize_Atmosphere_Code(); Initialize_BBox_Code(); Initialize_Lighting_Code(); Initialize_Mesh_Code(); Initialize_VLBuffer_Code(); Initialize_Radiosity_Code(); /* Always call this to print number of objects. */ Build_Bounding_Slabs(&Root_Object); /* Create the vista buffer. */ Build_Vista_Buffer(); /* Create the light buffers. */ Build_Light_Buffers(); /* Create blob queue. */ Init_Blob_Queue(); /* Save variable values. */ variable_store(STORE); /* 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(); } /* Get things ready for ray tracing (misc init, mem alloc) */ Initialize_Renderer(); /* 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] */ 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; } /* Get parsing time. */ STOP_TIME tparse = TIME_ELAPSED /* Get total parsing time. */ tparse_total += tparse; /* Store start time for trace. */ START_TIME if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME) { t_total=tparse_total+trender_total; SPLIT_TIME(t_total,&hours,&minutes,&seconds); Render_Info("\n %02ld:%02ld:%02.0f so far, ",hours,minutes,seconds); Render_Info("Rendering frame %d, going to %d.", opts.FrameSeq.FrameNumber, opts.FrameSeq.FinalFrame); } /* Start tracing. */ Stage = STAGE_RENDERING; POV_PRE_RENDER Status_Info ("\nRendering...\r"); /* 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(); } /* We're done. */ /* Record time so well spent before file close so it can be in comments */ STOP_TIME trender = TIME_ELAPSED /* Close out our file */ if (Output_File_Handle) { Close_File(Output_File_Handle); } Stage = STAGE_SHUTDOWN; POV_PRE_SHUTDOWN /* DESTROY lots of stuff */ Deinitialize_Atmosphere_Code(); Deinitialize_BBox_Code(); Deinitialize_Lighting_Code(); Deinitialize_Mesh_Code(); Deinitialize_VLBuffer_Code(); Deinitialize_Radiosity_Code(); Destroy_Blob_Queue(); Destroy_Light_Buffers(); Destroy_Vista_Buffer(); Destroy_Bounding_Slabs(); Destroy_Frame(); Terminate_Renderer(); FreeFontInfo(); Free_Iteration_Stack(); POV_POST_SHUTDOWN /* Get total render time. */ trender_total += trender; 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 ... */ PRINT_STATS(stats); if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME) { /* Add them up */ sum_statistics(totalstats, stats); /* ... and then clear them for the next frame */ init_statistics(stats); } /* Restore variable values. */ variable_store(RESTORE); } /***************************************************************************** * * FUNCTION * * fix_up_rendering_window * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Fix wrong window and mosaic preview values. * * CHANGES * * - * ******************************************************************************/ static void fix_up_rendering_window() { int temp; if (opts.First_Column_Percent > 0.0) opts.First_Column = (int) (Frame.Screen_Width * opts.First_Column_Percent); if (opts.First_Line_Percent > 0.0) opts.First_Line = (int) (Frame.Screen_Height * opts.First_Line_Percent); /* The decrements are a fudge factor that used to be in OPTIN.C * but it messed up Write_INI_File so its moved here. */ if (opts.First_Column <= 0) opts.First_Column = 0; else opts.First_Column--; if (opts.First_Line <= 0) opts.First_Line = 0; else opts.First_Line--; if ((opts.Last_Column == -1) && (opts.Last_Column_Percent <= 1.0)) opts.Last_Column = (int) (Frame.Screen_Width * opts.Last_Column_Percent); if ((opts.Last_Line == -1) && (opts.Last_Line_Percent <= 1.0)) opts.Last_Line = (int) (Frame.Screen_Height * opts.Last_Line_Percent); if (opts.Last_Line == -1) opts.Last_Line = Frame.Screen_Height; if (opts.Last_Column == -1) opts.Last_Column = Frame.Screen_Width; if (opts.Last_Column < 0 || opts.Last_Column > Frame.Screen_Width) opts.Last_Column = Frame.Screen_Width; if (opts.Last_Line > Frame.Screen_Height) opts.Last_Line = Frame.Screen_Height; /* Fix up Mosaic Preview values */ opts.PreviewGridSize_Start=max(1,opts.PreviewGridSize_Start); opts.PreviewGridSize_End=max(1,opts.PreviewGridSize_End); if ((temp=closest_power_of_2((unsigned)opts.PreviewGridSize_Start))!=opts.PreviewGridSize_Start) { Warning(0.0,"Preview_Start_Size must be a power of 2. Changing to %d.\n",temp); opts.PreviewGridSize_Start=temp; } if ((temp=closest_power_of_2((unsigned)opts.PreviewGridSize_End))!=opts.PreviewGridSize_End) { Warning(0.0,"Preview_End_Size must be a power of 2. Changing to %d.\n",temp); opts.PreviewGridSize_End=temp; } /* End must be less than or equal to start */ if (opts.PreviewGridSize_End > opts.PreviewGridSize_Start) opts.PreviewGridSize_End = opts.PreviewGridSize_Start; if (opts.PreviewGridSize_Start > 1) { opts.PreviewGridSize_End=max(opts.PreviewGridSize_End,2); opts.Options |= PREVIEW; } else { opts.Options &= ~PREVIEW; } /* Set histogram size here so it is available for Print_Options, and * make sure that it has an integer number of pixels/bucket. */ if (opts.histogram_on) { if (opts.histogram_x == 0 || opts.histogram_x > Frame.Screen_Width) opts.histogram_x = Frame.Screen_Width; else if (opts.histogram_x < Frame.Screen_Width) opts.histogram_x = Frame.Screen_Width / ((Frame.Screen_Width + opts.histogram_x - 1) / opts.histogram_x); if (opts.histogram_y == 0 || opts.histogram_y > Frame.Screen_Height) opts.histogram_y = Frame.Screen_Height; else if (opts.histogram_y < Frame.Screen_Height) opts.histogram_y = Frame.Screen_Height / ((Frame.Screen_Height + opts.histogram_y - 1) /opts.histogram_y); } } /***************************************************************************** * * FUNCTION * * fix_up_animation_values * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Validate animation parameters, compute subset values * * CHANGES * * - * ******************************************************************************/ static void fix_up_animation_values() { float ClockDiff; int FrameDiff; int FrameIncr; float ClockPerFrameIncr; int NumFrames; if (opts.FrameSeq.FinalFrame != -1) { opts.FrameSeq.FrameType = FT_MULTIPLE_FRAME; if (opts.FrameSeq.Clock_Value != 0.0) { Warning(0.0,"Attempted to set single clock value in multi frame\nanimation. Clock value overridden.\n"); } } else { if (opts.FrameSeq.Clock_Value != 0.0) { opts.FrameSeq.FrameType = FT_SINGLE_FRAME; } } if (opts.FrameSeq.FrameType == FT_SINGLE_FRAME) { /* * These are dummy values that will work for single_frame, * even in an animation loop. */ opts.FrameSeq.InitialFrame = 0; opts.FrameSeq.FinalFrame = 0; opts.FrameSeq.InitialClock = opts.FrameSeq.Clock_Value; opts.FrameSeq.FinalClock = 0.0; } else { /* FrameType==FT_MULTIPLE_FRAME */ if(opts.FrameSeq.InitialFrame == -1) { opts.FrameSeq.InitialFrame = 1; } if (opts.FrameSeq.FinalFrame < opts.FrameSeq.InitialFrame) { Error("Final frame %d is less than Start Frame %d.\n", opts.FrameSeq.FinalFrame, opts.FrameSeq.InitialFrame); } ClockDiff = opts.FrameSeq.FinalClock-opts.FrameSeq.InitialClock; if (opts.Options & CYCLIC_ANIMATION) { FrameDiff = opts.FrameSeq.FinalFrame-opts.FrameSeq.InitialFrame+1; } else { FrameDiff = opts.FrameSeq.FinalFrame-opts.FrameSeq.InitialFrame; } ClockPerFrameIncr = (FrameDiff == 0) ? 0 : (ClockDiff/FrameDiff); /* Calculate width, which is an integer log10 */ NumFrames = opts.FrameSeq.FinalFrame; opts.FrameSeq.FrameNumWidth = 1; while (NumFrames >= 10) { opts.FrameSeq.FrameNumWidth++; NumFrames = NumFrames / 10; } if (opts.FrameSeq.FrameNumWidth > POV_NAME_MAX-1) { Error("Can't render %d frames requiring %d chars with %d width filename.\n", opts.FrameSeq.FinalFrame - opts.FrameSeq.InitialFrame + 1, opts.FrameSeq.FrameNumWidth, POV_NAME_MAX); } /* STARTING FRAME SUBSET */ if (opts.FrameSeq.SubsetStartPercent != DBL_VALUE_UNSET) { FrameIncr = FrameDiff * opts.FrameSeq.SubsetStartPercent + 0.5; /* w/rounding */ opts.FrameSeq.SubsetStartFrame = opts.FrameSeq.InitialFrame + FrameIncr; } if (opts.FrameSeq.SubsetStartFrame != INT_VALUE_UNSET) { NumFrames = opts.FrameSeq.SubsetStartFrame - opts.FrameSeq.InitialFrame; opts.FrameSeq.InitialFrame = opts.FrameSeq.SubsetStartFrame; opts.FrameSeq.InitialClock = opts.FrameSeq.InitialClock + NumFrames * ClockPerFrameIncr; } /* ENDING FRAME SUBSET */ if (opts.FrameSeq.SubsetEndPercent != DBL_VALUE_UNSET) { /* * By this time, we have possibly lost InitialFrame, so we calculate * it via FinalFrame-FrameDiff */ FrameIncr = FrameDiff * opts.FrameSeq.SubsetEndPercent + 0.5; /* w/rounding */ opts.FrameSeq.SubsetEndFrame = (opts.FrameSeq.FinalFrame - FrameDiff) + FrameIncr; } if (opts.FrameSeq.SubsetEndFrame != INT_VALUE_UNSET) { NumFrames = opts.FrameSeq.FinalFrame - opts.FrameSeq.SubsetEndFrame; opts.FrameSeq.FinalFrame = opts.FrameSeq.SubsetEndFrame; opts.FrameSeq.FinalClock = opts.FrameSeq.FinalClock - NumFrames * ClockPerFrameIncr; } /* * Now that we have everything calculated, we check FinalFrame * and InitialFrame one more time, in case the subsets messed them up */ if (opts.FrameSeq.FinalFrame < opts.FrameSeq.InitialFrame) { Error("Final frame %d is less than Start Frame %d\ndue to bad subset specification.\n", opts.FrameSeq.FinalFrame, opts.FrameSeq.InitialFrame); } } /* Needed for pre-render shellout fixup */ opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame; opts.FrameSeq.Clock_Value = opts.FrameSeq.InitialClock; } /***************************************************************************** * * FUNCTION * * fix_up_scene_name * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Strip path and extention of input file to create scene name * * CHANGES * ******************************************************************************/ static void fix_up_scene_name() { int i, l; char temp[FILE_NAME_LENGTH]; if ((l=strlen(opts.Input_File_Name)-1)<1) { strcpy(opts.Scene_Name,opts.Input_File_Name); return; } strcpy(temp,opts.Input_File_Name); for (i=l;i>0;i--) { if (temp[i]==FILENAME_SEPARATOR) { break; } if (temp[i]=='.') { temp[i]=0; break; } } i=strlen(temp)-1; while ((i>0) && (temp[i]!=FILENAME_SEPARATOR)) i--; if (temp[i]==FILENAME_SEPARATOR) i++; strcpy(opts.Scene_Name,&(temp[i])); } /***************************************************************************** * * FUNCTION * * set_output_file_handle * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Set the output file handle according to the file type used. * * CHANGES * * Oct 95 - Removed test where the output file handle was only set if * output_to_file was TRUE. The output file handle structure * contains a pointer to read line, which is used by the continue * trace option. If you tried a continue trace with file output * manually turned OFF, then a GPF would occur due to a call to a * NULL function pointer. * ******************************************************************************/ static void set_output_file_handle() { char *def_ext = NULL; char temp[FILE_NAME_LENGTH]; switch (opts.OutputFormat) { case '\0': case 's' : case 'S' : Output_File_Handle = GET_SYS_FILE_HANDLE(); def_ext=SYS_DEF_EXT; break; case 't' : case 'T' : case 'c' : case 'C' : Output_File_Handle = Get_Targa_File_Handle(); def_ext=".tga"; break; case 'p' : case 'P' : Output_File_Handle = Get_PPM_File_Handle(); def_ext=".ppm"; break; case 'n' : case 'N' : Output_File_Handle = Get_Png_File_Handle(); def_ext=".png"; break; case 'd' : case 'D' : Error ("Dump format no longer supported.\n"); break; case 'r' : case 'R' : Error ("Raw format no longer supported.\n"); break; default : Error ("Unrecognized output file format %c.\n", opts.OutputFormat); } Output_File_Handle->file_type = IMAGE_FTYPE; strcpy(temp,opts.Output_File_Name); POV_SPLIT_PATH(temp,opts.Output_Path,opts.Output_File_Name); if (opts.Output_File_Name[0] == '\0') { sprintf(opts.Output_File_Name, "%s%s",opts.Scene_Name,def_ext); } else if (!(opts.Options & TO_STDOUT)) { if (!Has_Extension(opts.Output_File_Name)) { strcat(opts.Output_File_Name, def_ext); } } strcpy(opts.Output_Numbered_Name,opts.Output_File_Name); } /***************************************************************************** * * FUNCTION * * setup_output_file_name * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Determine the file name for this frame. For an animation, the frame * number is inserted into the file name. * * CHANGES * * Jan-97 [esp] Added conditional after getcwd, because Metrowerks getcwd * function appends a path separator on output. * ******************************************************************************/ static void setup_output_file_name() { char number_string[10]; char separator_string[2] = {FILENAME_SEPARATOR, 0} ; char *plast_period; int available_characters; int ilast_period; int fname_chars; /* This will create the real name for the file */ if(opts.FrameSeq.FrameType!=FT_MULTIPLE_FRAME || opts.Options & TO_STDOUT) { strcpy(opts.Output_Numbered_Name,opts.Output_File_Name); } else { /* * This is the maximum number of characters that can be used of the * original filename. This will ensure that enough space is available * for the frame number in the filename */ available_characters = POV_NAME_MAX-opts.FrameSeq.FrameNumWidth; plast_period = strrchr(opts.Output_File_Name, '.'); if (plast_period == NULL) { Error("Illegal file name %s -- no extension.\n", opts.Output_File_Name); } ilast_period = plast_period - opts.Output_File_Name; fname_chars = ilast_period; if (fname_chars > available_characters) { /* Only give the warning once */ if (opts.FrameSeq.FrameNumber == opts.FrameSeq.InitialFrame) { Warning(0.0, "Need to cut the output filename by %d characters.\n", ilast_period - available_characters); } fname_chars = available_characters; } /* Perform actual generation of filename */ strncpy(opts.Output_Numbered_Name, opts.Output_File_Name, (unsigned)fname_chars); /* strncpy doesn't terminate if strlen(opts.Output_File_Name) c2) { return(1); } } if (*s1 == '\0') { if (*s2 == '\0') { return(0); } else { return(-1); } } else { return(1); } } /***************************************************************************** * * FUNCTION * * Locate_File * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Find a file in the search path. * * CHANGES * * Apr 1996: Don't add trailing FILENAME_SEPARATOR if we are immediately * following DRIVE_SEPARATOR because of Amiga probs. [AED] * ******************************************************************************/ FILE *Locate_File (filename, mode, ext1, ext2, err_flag) char *filename, *mode, *ext1, *ext2; int err_flag; { int i,l1,l2; char pathname[FILE_NAME_LENGTH]; char file0[FILE_NAME_LENGTH]; char file1[FILE_NAME_LENGTH]; char file2[FILE_NAME_LENGTH]; FILE *f; if (Has_Extension(filename)) { l1=l2=0; } else { if ((l1 = strlen(ext1)) > 0) { strcpy(file1, filename); strcat(file1, ext1); } if ((l2 = strlen(ext2)) > 0) { strcpy(file2, filename); strcat(file2, ext2); } } /* Check the current directory first. */ if (l1) { if ((f = fopen(file1, mode)) != NULL) { return(f); } } if (l2) { if ((f = fopen(file2, mode)) != NULL) { return(f); } } if ((f = fopen(filename, mode)) != NULL) { return(f); } for (i = 0; i < opts.Library_Path_Index; i++) { strcpy(file0, opts.Library_Paths[i]); file0[strlen(file0)+1] = '\0'; if (file0[strlen(file0) - 1] != DRIVE_SEPARATOR) file0[strlen(file0)] = FILENAME_SEPARATOR; if (l1) { strcpy(pathname, file0); strcat(pathname, file1); if ((f = fopen(pathname, mode)) != NULL) { return(f); } } if (l2) { strcpy(pathname, file0); strcat(pathname, file2); if ((f = fopen(pathname, mode)) != NULL) { return(f); } } strcpy(pathname, file0); strcat(pathname, filename); if ((f = fopen(pathname, mode)) != NULL) { return(f); } } if (err_flag) { if (l1) { Error_Line("Could not find file '%s%s'\n",filename,ext1); } else { Error_Line("Could not find file '%s'\n",filename); } } return(NULL); } /***************************************************************************** * * FUNCTION * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * DESCRIPTION * * CHANGES * ******************************************************************************/ static int Has_Extension (name) char *name; { char *p; if (name!=NULL) { p=strrchr(name, '.'); if (p!=NULL) { if ((strlen(name)-(p-name))<=4) { return (TRUE); } } } return (FALSE); } /***************************************************************************** * * FUNCTION * * pov_shellout * * INPUT * * template_command - the template command string to execute * * OUTPUT * * RETURNS * * AUTHOR * * POV-Ray Team * * DESCRIPTION * * Execute the command line described by the string being passed in * * CHANGES * * - * ******************************************************************************/ SHELLRET pov_shellout (Type) SHELLTYPE Type; { char real_command[POV_MAX_CMD_LENGTH]; int i, j, l = 0; int length; SHELLRET Return_Code; char *s = NULL; char *template_command; if ( opts.Shellouts == NULL ) return(IGNORE_RET); template_command=opts.Shellouts[Type].Command; if ((length = strlen(template_command)) == 0) { return(IGNORE_RET); } switch(Type) { case PRE_SCENE_SHL: s="pre-scene"; break; case PRE_FRAME_SHL: s="pre-frame"; break; case POST_FRAME_SHL: s="post-frame"; break; case POST_SCENE_SHL: s="post-scene"; break; case USER_ABORT_SHL: s="user about"; break; case FATAL_SHL: s="fatal error"; break; case MAX_SHL: /* To remove warnings*/ break; } Status_Info("\nPerforming %s shell-out command",s); /* First, find the real command */ for (i = 0, j = 0; i < length; ) { if (template_command[i] == '%') { switch (toupper(template_command[i+1])) { case 'O': strncpy(&real_command[j], opts.Output_Numbered_Name, (unsigned)(l=strlen(opts.Output_Numbered_Name))); break; case 'P': strncpy(&real_command[j], opts.Output_Path,(unsigned)(l=strlen(opts.Output_Path))); break; case 'S': strncpy(&real_command[j], opts.Scene_Name, (unsigned)(l=strlen(opts.Scene_Name))); break; case 'N': sprintf(&real_command[j],"%d",opts.FrameSeq.FrameNumber); l = strlen(&real_command[j]); break; case 'K': sprintf(&real_command[j],"%f",opts.FrameSeq.Clock_Value); l = strlen(&real_command[j]); break; case 'H': sprintf(&real_command[j],"%d",Frame.Screen_Height); l = strlen(&real_command[j]); break; case 'W': sprintf(&real_command[j],"%d",Frame.Screen_Width); l = strlen(&real_command[j]); break; case '%': real_command[j]='%'; l=1; break; } j+=l; i+=2; /* we used 2 characters of template_command */ } else { real_command[j++]=template_command[i++]; } } real_command[j]='\0'; Return_Code=POV_SYSTEM(real_command); if (opts.Shellouts[Type].Inverse) { Return_Code=!Return_Code; } if (Return_Code) { if (Type < USER_ABORT_SHL) { switch(opts.Shellouts[Type].Ret) { case FATAL_RET: Error("Fatal error returned from shellout command."); break; case USER_RET: Check_User_Abort(TRUE); /* the TRUE forces user abort */ break; case QUIT_RET: Terminate_POV(0); break; case IGNORE_RET: case SKIP_ONCE_RET: case ALL_SKIP_RET: /* Added to remove warnings */ break; } } return(opts.Shellouts[Type].Ret); } return(IGNORE_RET); } /***************************************************************************** * * FUNCTION * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * DESCRIPTION * * CHANGES * ******************************************************************************/ static void init_shellouts() { int i; opts.Shellouts=POV_MALLOC(sizeof(SHELLDATA)*MAX_SHL,"shellout data"); for (i=0; i < MAX_SHL; i++) { opts.Shellouts[i].Ret=IGNORE_RET; opts.Shellouts[i].Inverse=FALSE; opts.Shellouts[i].Command[0]='\0'; } } /***************************************************************************** * * FUNCTION * * INPUT * * OUTPUT * * RETURNS * * AUTHOR * * DESCRIPTION * * CHANGES * ******************************************************************************/ static void destroy_shellouts() { if (opts.Shellouts != NULL) { POV_FREE(opts.Shellouts); } opts.Shellouts=NULL; } /***************************************************************************** * * FUNCTION * * closest_power_of_2 * * INPUT * * theNumber - the value to determine closest power of 2 for. * * OUTPUT * * RETURNS * * The closest power of two is returned, or zero if the * argument is less than or equal to zero. * * AUTHOR * * Eduard Schwan * * DESCRIPTION * * Decription: Find the highest positive power of 2 that is * less than or equal to the number passed. * * Input Output * ----- ------ * 0 0 * 1 1 * 2 2 * 3 2 * 8 8 * 9 8 * * CHANGES * * Aug 1994 : Created by Eduard. * ******************************************************************************/ static unsigned closest_power_of_2(theNumber) unsigned theNumber; { int PowerOf2Counter; /* do not handle zero or negative numbers for now */ if (theNumber <= 0) { return(0); } /* count the number in question down as we count up a power of 2 */ PowerOf2Counter = 1; while (theNumber > 1) { /* move our power of 2 counter bit up... */ PowerOf2Counter <<= 1; /* and reduce our test number by a factor of 2 two */ theNumber >>= 1; } return(PowerOf2Counter); } /***************************************************************************** * * FUNCTION * * pre_init_povray * * INPUT -- none * * OUTPUT * * RETURNS * * AUTHOR -- CEY * * DESCRIPTION * * This routine does essential initialization that is required before any * POV_MALLOC-like routines may be called and before any text streams * may be used. * * If you are using alt_main and need access to any part of the generic code * before alt_main is called, you MUST call this routine first! Also note * that it is safe to call it twice. If you don't call it, alt_main will. * It won't hurt if you both do it. * * NOTE: Terminate_POV de-initializes these features. Therefore you may * need to call it again between sucessive calls to alt_main. If you call * pre_init_povray but for some reason you abort and don't call alt_main, * then you should call Terminate_POV to clean up. * * CHANGES * Nov 1995 : Created by CEY * ******************************************************************************/ void pre_init_povray() { if (pre_init_flag==1234) { return; } /* Initialize memory. */ mem_init(); /* Initialize streams. In USERIO.C */ Init_Text_Streams(); init_shellouts(); pre_init_tokenizer (); pre_init_flag=1234; } void POV_Split_Path(s,p,f) char *s; char *p; char *f; { char *l; strcpy(p,s); if ((l=strrchr(p,FILENAME_SEPARATOR))==NULL) { if ((l=strrchr(p,DRIVE_SEPARATOR))==NULL) { strcpy(f,s); p[0]='\0'; return; } } l++; strcpy(f,l); *l='\0'; }