/* main.c: main program */ #include #include #include #include #include #include "config.h" #include "ui.h" #include "defaults.h" #include "options.h" #include "statistics.h" #include "camera.h" #include "render.h" #include "error.h" #include "scene.h" #include "geom.h" #include "patch_type.h" #include "patch_flags.h" #include "material.h" #include "edf.h" #include "surface.h" #include "readmgf.h" #ifndef NO_VRML #include "readvrml.H" #endif #include "pools.h" #include "patch.h" #include "cubature.h" #include "cluster.h" #include "radiance.h" #include "raytracing.h" #include "monitor.h" #include "vertex.h" #include "brep.h" #include "compound.h" #include "grid.h" #include "ipc.h" #include "renderhook.h" #include "tonemapping.h" #include "fileopts.h" #include "ui_pathdebug.h" #include "background_edf.h" /* statistics stuff */ int nrpatches, nrlightsources; double total_area; double average_direct_potential, total_direct_potential; double max_direct_potential, max_direct_importance; COLOR total_emitted_power, estimated_average_radiance; COLOR average_reflectivity, max_selfemitted_radiance, max_selfemitted_power; double reference_luminance; /* program name, as determines from zero-th command line argument */ char *progname; /* default alarm signal handler: if there is not one set explicitely in the * program, the program would be killed if an ALARM signal is raised. Alarm * signals are used for waking up now and then during the computations. */ static void default_alarm_handler(int sig) { /* does nothing, except catching the ALARM signal. If we wouldn't catch it, * the program would be terminated instantly. */ time_t t = time(NULL); fprintf(stderr, "%s%s line %d: You're lucky that *I* catched that alarm signal ...\n", asctime(localtime(&t)), __FILE__, __LINE__); } static void PatchAccumulateStats(PATCH *patch) { COLOR E = PatchAverageEmittance(patch, ALL_COMPONENTS), R = PatchAverageNormalAlbedo(patch, BSDF_ALL_COMPONENTS), power; total_area += patch->area; COLORSCALE(patch->area, E, power); COLORADD(total_emitted_power, power, total_emitted_power); COLORADDSCALED(average_reflectivity, patch->area, R, average_reflectivity); /* convert radiant exitance to exitant radiance */ COLORSCALE((1./M_PI), E, E); COLORMAX(E, max_selfemitted_radiance, max_selfemitted_radiance); COLORMAX(power, max_selfemitted_power, max_selfemitted_power); } static void ComputeSomeSceneStats(void) { VECTOR zero; COLOR one, average_absorption, BP; COLORSETMONOCHROME(one, 1.); VECTORSET(zero, 0, 0, 0); /* initialize */ COLORCLEAR(total_emitted_power); COLORCLEAR(average_reflectivity); COLORCLEAR(max_selfemitted_radiance); COLORCLEAR(max_selfemitted_power); total_area = 0.; /* accumulate */ PatchListIterate(Patches, PatchAccumulateStats); /* averages ... */ COLORSCALEINVERSE(total_area, average_reflectivity, average_reflectivity); COLORSUBTRACT(one, average_reflectivity, average_absorption); COLORSCALEINVERSE(M_PI * total_area, total_emitted_power, estimated_average_radiance); /* include background radiation */ BP = BackgroundPower(Background, &zero); COLORSCALE(1. / (4 * M_PI), BP, BP); COLORADD(total_emitted_power, BP, total_emitted_power); COLORADD(estimated_average_radiance, BP, estimated_average_radiance); COLORDIV(estimated_average_radiance, average_absorption, estimated_average_radiance); total_direct_potential = max_direct_potential = average_direct_potential = max_direct_importance = 0.; } /* adds the background to the global light source patch list */ void AddBackgroundToLightSourceList(void) { if (Background) { /* add to list */ LightSourcePatches = PatchListAdd(LightSourcePatches, Background->bkgPatch); } } /* adds the patch to the global light source patch list if the patch is on * a light source (i.e. when the surfaces material has a non-null edf). */ static void AddPatchToLightSourceListIfLightSource(PATCH *patch) { if (patch->surface->material->edf) { LightSourcePatches = PatchListAdd(LightSourcePatches, patch); nrlightsources++; } } /* Build the global light source patch list. */ static void BuildLightSourcePatchList(void) { LightSourcePatches = PatchListCreate(); nrlightsources = 0; PatchListIterate(Patches, AddPatchToLightSourceListIfLightSource); /* add background if present */ AddBackgroundToLightSourceList(); nrlightsources++; } /* Tries to read the scene in the given file. Returns False if not succesful. * Returns True if succesful. There's nothing GUI specific in this function. * When a file cannot be read, the current scene is restored. */ Boolean ReadFile(char *filename) { char *dot, *slash, *extension; FILE *input; GEOMLIST *oWorld, *oClusteredWorld; GEOM *oClusteredWorldGeom; MATERIALLIST *oMaterialLib; PATCHLIST *oPatches, *oLightSourcePatches; GRID *oWorldGrid; RADIANCEMETHOD *oRadiance; RAYTRACINGMETHOD *oRayTracing; BACKGROUND *oBackground; int opatchid, onrpatches; clock_t t, last; /* check whether the file can be opened if not reading from stdin */ if (filename[0] != '#') { if ((input = fopen(filename, "r")) == (FILE *)NULL || fgetc(input) == EOF) { if (input) fclose(input); Error(NULL, "Can't open file '%s' for reading", filename); return False; } fclose(input); } /* get current directory from the filename */ current_directory = (char *)Alloc(strlen(filename)+1); sprintf(current_directory, "%s", filename); if ((slash = strrchr(current_directory, '/')) != NULL) *slash = '\0'; else *current_directory = '\0'; ErrorReset(); /* terminate any active radiance or raytracing methods */ fprintf(stderr, "Terminating current radiance/raytracing method ... \n"); oRadiance = Radiance; SetRadianceMethod((RADIANCEMETHOD *)NULL); oRayTracing = RayTracing; SetRayTracing((RAYTRACINGMETHOD *)NULL); /* save the current scene so it can be restored if errors occur when * reading the new scene */ fprintf(stderr, "Saving current scene ... \n"); oWorld = World; World = GeomListCreate(); oMaterialLib = MaterialLib; MaterialLib = MaterialListCreate(); oPatches = Patches; Patches = PatchListCreate(); opatchid = PatchGetNextID(); PatchSetNextID(1); onrpatches = nrpatches; oClusteredWorld = ClusteredWorld; ClusteredWorld = GeomListCreate(); oClusteredWorldGeom = ClusteredWorldGeom; oLightSourcePatches = LightSourcePatches; oWorldGrid = WorldGrid; oBackground = Background; Background = (BACKGROUND *)NULL; /* read the MGF file. The result is a new World and MaterialLib if * everything goes well. */ fprintf(stderr, "Reading the scene from file '%s' ... \n", filename); last = clock(); if ((dot = strrchr(filename, '.')) != NULL) extension = dot+1; else extension = "mgf"; if (strcmp(extension, "gz") == 0 || strcmp(extension, "Z") == 0 || strcmp(extension, "bz") == 0 || strcmp(extension, "bz2") == 0) { /* compressed file, find real extension */ do { dot--; } while (dot>filename && *dot!='.'); if (*dot == '.') extension = dot+1; } if (strncmp(extension, "mgf", 3) == 0) ReadMgf(filename); #ifndef NO_VRML else if (strncmp(extension, "wrl", 3) == 0 || strncmp(extension, "vrml", 4) == 0) ReadVrml(filename); #endif else { #ifndef NO_VRML Error(NULL, "Unsupported scene format (no MGF or VRML)"); #else Error(NULL, "Unsupported scene format (no MGF)"); #endif } t = clock(); fprintf(stderr, "Reading took %g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; Free(current_directory, strlen(filename)+1); current_directory = NULL; /* check for errors */ if (!World) { /* restore the old scene */ fprintf(stderr, "Restoring old scene ... "); fflush(stderr); GeomListIterate(World, GeomDestroy); GeomListDestroy(World); World = oWorld; MaterialListIterate(MaterialLib, MaterialDestroy); MaterialListDestroy(MaterialLib); MaterialLib = oMaterialLib; Patches = oPatches; PatchSetNextID(opatchid); nrpatches = onrpatches; ClusteredWorld = oClusteredWorld; ClusteredWorldGeom = oClusteredWorldGeom; LightSourcePatches = oLightSourcePatches; WorldGrid = oWorldGrid; Background = oBackground; SetRadianceMethod(oRadiance); SetRayTracing(oRayTracing); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; fprintf(stderr, "Done.\n"); if (!ErrorOccurred()) Error(NULL, "Empty world"); return False; /* not succesful */ } /* initialize "monitor" */ MonitorInit(); /* dispose of the old scene */ fprintf(stderr, "Disposing of the old scene ... "); fflush(stderr); if (Radiance) Radiance->Terminate(); if (RayTracing) RayTracing->Terminate(); PatchListDestroy(oPatches); PatchListDestroy(oLightSourcePatches); GeomListIterate(oWorld, GeomDestroy); GeomListDestroy(oWorld); if (oClusteredWorldGeom) GeomDestroy(oClusteredWorldGeom); if (oBackground) oBackground->methods->Destroy(oBackground->data); DestroyGrid(oWorldGrid); MaterialListIterate(oMaterialLib, MaterialDestroy); MaterialListDestroy(oMaterialLib); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; /* build the new patch list, this is duplicating already available * information and as such potentially dangerous, but we need it * so many times, so ... */ fprintf(stderr, "Building patch list ... "); fflush(stderr); Patches = BuildPatchList(World, PatchListCreate()); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; /* destilate the list of patches on light sources from the above patch list. */ fprintf(stderr, "Building light source patch list ... "); fflush(stderr); BuildLightSourcePatchList(); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; /* build a cluster hierarchy for the new scene. */ fprintf(stderr, "Building cluster hierarchy ... "); fflush(stderr); ClusteredWorldGeom = CreateClusterHierarchy(Patches); if (GeomIsCompound(ClusteredWorldGeom)) ClusteredWorld = (GEOMLIST *)(ClusteredWorldGeom->obj); else { /* small memory leak here ... but exceptional situation! */ ClusteredWorld = GeomListAdd(GeomListCreate(), ClusteredWorldGeom); Warning(NULL, "Strange clusters for this world ..."); } t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; /* engridding the thing */ WorldGrid = CreateGrid(ClusteredWorldGeom); t = clock(); fprintf(stderr, "Engridding took %g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; /* estimate average radiance, for radiance to display RGB conversion */ fprintf(stderr, "Computing some scene statistics ... "); fflush(stderr); nrpatches = nrelements; ComputeSomeSceneStats(); reference_luminance = 5.42 * ((1.-ColorGray(average_reflectivity)) * ColorLuminance(estimated_average_radiance)); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; /* Initialize tone mapping. */ fprintf(stderr, "Initializing tone mapping ... "); fflush(stderr); InitToneMapping(); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; printf("Stats: total_emitted_power .............: %f W\n" " estimated_average_illuminance ...: %f W/sr\n" " average_reflectivity ............: %f\n" " max_selfemitted_radiance ........: %f W/sr\n" " max_selfemitted_power ...........: %f W\n" " adaptation_luminance ............: %f cd/m2\n" " total_area ......................: %f m2\n", ColorGray(total_emitted_power), ColorGray(estimated_average_radiance), ColorGray(average_reflectivity), ColorGray(max_selfemitted_radiance), ColorGray(max_selfemitted_power), tmopts.lwa, total_area); /* initialize radiance for the freshly loaded scene */ if (oRadiance) { fprintf(stderr, "Initializing radiance computations ... "); fflush(stderr); SetRadianceMethod(oRadiance); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; } if (oRayTracing) { fprintf(stderr, "Initializing raytracing computations ... \n"); SetRayTracing(oRayTracing); t = clock(); fprintf(stderr, "%g secs.\n", (float)(t-last)/(float)CLOCKS_PER_SEC); last = t; } /* Remove possible renderhooks */ RemoveAllRenderHooks(); /* Re-initialize path debug */ InitPathDebug(); fprintf(stderr, "Initialisations done.\n"); UpdateFileStats(); /* set main window title to filename */ #ifndef LONG_FNTITLE { char *slash = strrchr(filename, '/'); SetWindowTitle(slash ? slash+1 /*skip '/'*/ : filename); } #else SetWindowTitle(filename); #endif return True; /* succes */ } /* global initializations */ void Init(void) { signal(SIGALRM, default_alarm_handler); /* Transforms the cubature rules for quadrilaterals to be over the domain [0,1]^2 * instead of [-1,1]^2. See cubature.[ch] */ FixCubatureRules(); monochrome = DEFAULT_MONOCHROME; force_onesided_surfaces = DEFAULT_FORCE_ONESIDEDNESS; nqcdivs = DEFAULT_NQCDIVS; MgfDefaults(); #ifndef NO_VRML VrmlDefaults(); #endif RenderingDefaults(); ToneMapDefaults(); CameraDefaults(); RadianceDefaults(); RayTracingDefaults(); InterfaceDefaults(); IpcDefaults(); /* Default vertex compare flags: both location and normal is relevant. Two * vertices without normal, but at the same location, are to be considered * different. */ VertexSetCompareFlags(VCMP_LOCATION | VCMP_NORMAL); /* Specify what routines to be used to compare vertices when using * BREP_VERTEX_OCTREEs */ BrepSetVertexCompareRoutine((BREP_COMPARE_FUNC)VertexCompare); BrepSetVertexCompareLocationRoutine((BREP_COMPARE_FUNC)VertexCompareLocation); } static void SetSeed(void *pseed) { int seedval = *(int *)pseed; srand48(seedval); } static void ReadMGFFromStdin(void *bla) { ReadFile("#.mgf"); } #ifndef NO_VRML static void ReadVRMLFromStdin(void *bla) { ReadFile("#.wrl"); } #endif static void ShowUsage(void *bla); static int yes=1, no=0; static void ForceOnesidedOption(void *value) { force_onesided_surfaces = *(int *)value; } static void MonochromeOption(void *value) { monochrome = *(int *)value; } static CMDLINEOPTDESC globalOptions[] = { {"-mgf", 3, TYPELESS, NULL, ReadMGFFromStdin, "-mgf \t\t: read MGF file from standard input"}, #ifndef NO_VRML {"-vrml", 3, TYPELESS, NULL, ReadVRMLFromStdin, "-vrml \t\t: read VRML'97 file from standard input"}, #endif {"-nqcdivs", 3, Tint, &nqcdivs, DEFAULT_ACTION, "-nqcdivs \t: number of quarter circle divisions"}, {"-force-onesided", 10, TYPELESS, &yes, ForceOnesidedOption, "-force-onesided\t\t: force one-sided surfaces"}, {"-dont-force-onesided", 14, TYPELESS, &no, ForceOnesidedOption, "-dont-force-onesided\t: allow two-sided surfaces"}, {"-monochromatic", 5, TYPELESS, &yes, MonochromeOption, "-monochromatic \t\t: convert colors to shades of grey"}, {"-seed", 2, Tint, NULL, SetSeed, "-seed \t\t: set seed for random number generator"}, {"-help", 2, TYPELESS, NULL, ShowUsage, "-help \t\t: show program usage and command line options"}, {NULL , 0, TYPELESS, NULL, DEFAULT_ACTION, NULL } }; /* show program usage */ static void ShowUsage(void *bla) { printf("Usage: %s [options] [filename]\n", progname); printf("\nGeneral options:\n"); PrintOptions(stdout, globalOptions); PrintMgfOptions(stdout); #ifndef NO_VRML PrintVrmlOptions(stdout); #endif PrintCameraOptions(stdout); PrintRenderingOptions(stdout); PrintToneMapOptions(stdout); PrintRadianceOptions(stdout); PrintRayTracingOptions(stdout); PrintInterfaceOptions(stdout); PrintIpcOptions(stdout); exit(0); } /* processes command line arguments not recognized by the Xt GUI toolkit */ static void ParseGlobalOptions(int *argc, char **argv) { /* copy program name from zeroeth argument */ progname = strdup(argv[0]); ParseMgfOptions(argc, argv); #ifndef NO_VRML ParseVrmlOptions(argc, argv); #endif ParseRenderingOptions(argc, argv); ParseToneMapOptions(argc, argv); ParseCameraOptions(argc, argv); ParseRadianceOptions(argc, argv); ParseRayTracingOptions(argc, argv); ParseInterfaceOptions(argc, argv); ParseIpcOptions(argc, argv); ParseOptions(globalOptions, argc, argv); /* this one comes last in order to * have all other options parsed before * reading input from stdin. */ } int main(int argc, char **argv) { /* various global initialisations: sets defaults for all possible things. */ Init(); /* process command line arguments */ ParseGlobalOptions(&argc, argv); StartUserInterface(&argc, argv); /* never returns. */ return 0; }