00001 /*
00002 File: RadMain.cc
00003
00004 Function: Implements "Radiator"
00005
00006 Provides command line-driven program for running
00007 various radiosity methods.
00008
00009 Author(s): Andrew Willmott
00010
00011 Copyright: (c) 1997-2000, Andrew Willmott
00012
00013 Notes:
00014 */
00015
00016 #ifdef CL_HAS_NEW
00017 #include <new>
00018 #endif
00019 #include <unistd.h>
00020 #include <fstream.h>
00021 #include <signal.h>
00022
00023 #include "cl/ArgParse.h"
00024
00025 #include "RadMethod.h"
00026
00027 #include "cl/String.h"
00028 #include "gcl/Avars.h"
00029 #include "gcl/SceneLang.h"
00030 #include "gcl/Readers.h"
00031
00032 #include "BuildDate.h"
00033
00034 class RadMain
00035 {
00036 public:
00037 Void SetOptions(Int argc, Char *argv[]);
00038 Void DoRadiosity();
00039
00040 RadControl radOpts;
00041 Int size;
00042 Int dumpObj;
00043 Int dumpSL;
00044 Int noDirect;
00045 Int normalise;
00046 Char *sceneFile;
00047 String outFile;
00048
00049 static Int frame;
00050 };
00051
00052 static char *defMethodName[] =
00053 {
00054 "none",
00055 "mat",
00056 "prog",
00057 "hprog",
00058 "hier",
00059 "ana"
00060 };
00061
00062 static char *defBasisName[] =
00063 {
00064 "none",
00065 "haar",
00066 "f2",
00067 "f3",
00068 "m2",
00069 "m3"
00070 };
00071
00072
00073 // --- Utilities --------------------------------------------------------------
00074
00075 static void LogToFile(const FileName &logFile)
00076 // capture stdout & copy it to a run file
00077 {
00078 int filedes[2], count;
00079 char buffer[1024];
00080 FILE *log;
00081
00082 if (pipe(filedes) == -1)
00083 {
00084 perror("logger: pipe");
00085 return;
00086 }
00087
00088 fflush(stdout);
00089
00090 if (fork() != 0)
00091 {
00092 if (dup2(filedes[1], fileno(stdout)) == -1)
00093 perror("logger: dup");
00094 if (close(filedes[0]) == -1)
00095 perror("logger: c0");
00096
00097 return;
00098 }
00099
00100 close(filedes[1]);
00101 log = logFile.FOpen("w");
00102
00103 while ((count = read(filedes[0], buffer, 1024)) > 0)
00104 {
00105 write(1, buffer, count);
00106 fwrite(buffer, 1, count, log);
00107 }
00108 if (count == -1)
00109 perror("logger: read");
00110
00111 fclose(log);
00112 close(filedes[0]);
00113
00114 exit(0);
00115 }
00116
00117 static Void RadHandleInterrupt(Int a)
00118 {
00119 // On reception of a SIGHUP, stop simulation.
00120 cerr << "received interrupt " << a << ": stopping simulation" << endl;
00121 gRadControl->limitTime = 0;
00122 }
00123
00124 static Void RadHandleCont(Int a)
00125 {
00126 // On reception of a SIGCONT, print status info
00127 cerr << "received interrupt " << a << ": printing status" << endl;
00128 gRadControl->radObject->lastTime = gRadControl->radObject->totTime
00129 - gRadControl->sliceTime;
00130 }
00131
00132 static Array<Char*> gAvarNames;
00133 static Array<GCLReal> gAvarVals;
00134
00135 static Int avarArg(int argc, char **argv)
00136 {
00137 Int i;
00138 GCLReal val;
00139
00140 if (argc % 2 == 1)
00141 {
00142 cerr << "Wrong number of avar arguments." << endl;
00143 exit(1);
00144 }
00145
00146 for (i = 0; i < argc; i += 2)
00147 {
00148 val = atof(argv[i + 1]);
00149 if (argv[i] == StrConst("frame"))
00150 RadMain::frame = (Int) val;
00151 gAvarNames.Append(argv[i]);
00152 gAvarVals.Append(val);
00153 }
00154
00155 return(0);
00156 }
00157
00158
00159 // --- RadMain methods --------------------------------------------------------
00160
00161 Int RadMain::frame = -1;
00162
00163 Void RadMain::SetOptions(Int argc, Char *argv[])
00164 {
00165 Int hier, prog, mat, hprog, ana;
00166 Int haar, f2, f3, m2, m3, noClusFlag;
00167 Int schedFlag, interFlag, twoStageFlag, fcrFlag = false;
00168 Int ambient, viq, bestPass, bestVisPass;
00169 Int noBF, noGraded, noAnchor, refAllLinks, conjGrad;
00170 Int visnone, vis1, vis16, vis44;
00171 Int updateScene, dumpMatrix, dumpScenes, dumpTree;
00172 Int meshRnd, meshNL, meshFix, meshNoConnect, meshNoGrid;
00173 Int version;
00174 Double meshComp, rtComp;
00175 String usage;
00176 Arg_form *arg_format;
00177
00178 // Set default arguments
00179
00180 size = 400;
00181 frame = -1;
00182 sceneFile = 0;
00183 radOpts.outFile = 0;
00184 meshComp = 1.0;
00185 rtComp = -1.0;
00186
00187 usage.Printf("Usage: %s [options], where options are as follows:",
00188 argv[0]);
00189
00190 // Create arg form
00191
00192 arg_format = arg_to_form(0,
00193 "-v", ARG_FLAG(&version), "show current version",
00194 "[%S]", &sceneFile, "input scene file",
00195
00196 // methods
00197
00198 "",
00199 "\n--- Methods --------------------------------------------------------------------\n",
00200 "-mat", ARG_FLAG(&mat), "use matrix radiosity",
00201 "-prog", ARG_FLAG(&prog), "use progressive radiosity",
00202 "-hprog", ARG_FLAG(&hprog), "use progressive radiosity with substructuring",
00203 "-hier", ARG_FLAG(&hier), "use hierarchical radiosity",
00204 "-ana", ARG_FLAG(&ana), "use 1st-order analytical solver",
00205
00206 // method params
00207
00208 "",
00209 "\n--- Params ---------------------------------------------------------------------\n",
00210 "-sub %F", &radOpts.patchSubdivs, "set patch subdivision density",
00211 "-esub %F", &radOpts.eltSubdivs, "set element subdivision density",
00212 "-alpha %F", &radOpts.alpha, "set alpha",
00213 "-error %F", &radOpts.error, "set termination error",
00214 "-ferr %F", &radOpts.kFError, "set ff error",
00215 "-aerr %F", &radOpts.kAError, "set min patch size",
00216 "-derr %F", &radOpts.dFError, "set singularity threshold",
00217
00218 // bases
00219
00220
00221 // visibility, form factor eval
00222
00223 "",
00224 "\n--- Visibility------------------------------------------------------------------\n",
00225 "-verr %F", &radOpts.visError, "wavelet visibility error",
00226 "-quadLevel %d", &radOpts.quadLevel, "quadrature level",
00227 "-visInQuad", ARG_FLAG(&viq), "do visibility testing in quadrature",
00228
00229 "-vis_none", ARG_FLAG(&visnone), "no visibility testing",
00230 "-vis_1", ARG_FLAG(&vis1), "single ray visibility",
00231 "-vis_16", ARG_FLAG(&vis16), "use 16 rays from source -> centre of dest",
00232 "-vis_44", ARG_FLAG(&vis44), "use 16 rays distributed over both patches",
00233 "-visReuseArea %F", &radOpts.forceVisReuseArea,
00234 "area below which to reuse visibility samples",
00235 "-favourReceiver %F", &radOpts.favourReceiver,
00236 "amount by which to favour subdividing receiver",
00237
00238 // meshing
00239
00240 "",
00241 "\n--- Meshing --------------------------------------------------------------------\n",
00242 "-mesh_share", ARG_FLAG(&meshFix), "ensure all coincident mesh vertices are shared",
00243 "-mesh_noAnchor", ARG_FLAG(&noAnchor), "don't enforce elimination of t-vertices",
00244 "-mesh_noGrading", ARG_FLAG(&noGraded), "don't enforce a graded mesh",
00245 "-mesh_noConnect", ARG_FLAG(&meshNoConnect), "don't calculate mesh connectivity",
00246 "-mesh_noGrid", ARG_FLAG(&meshNoGrid), "don't use gridding, only hierarchical refinement",
00247 "-mesh_nonLin", ARG_FLAG(&meshNL), "mesh more heavily at edges",
00248 "-mesh_random", ARG_FLAG(&meshRnd), "vary 'sub' parameter randomly",
00249
00250 // MRM complexity
00251
00252 "",
00253 "\n--- Complexity -----------------------------------------------------------------\n",
00254 "-meshComp %F", &meshComp, "complexity for creating radiosity mesh",
00255 "-rtComp %F", &rtComp, "complexity for raytracing (if different.)",
00256
00257 // misc
00258
00259 "",
00260 "\n--- Misc -----------------------------------------------------------------------\n",
00261 "-conjGrad", ARG_FLAG(&conjGrad), "use conjugate gradient solver (matrix radiosity)",
00262 "-ambient", ARG_FLAG(&ambient), "add ambient term",
00263 "-maxShots %d", &radOpts.maxShots, "max shooting steps to perform",
00264 "-noDirect", ARG_FLAG(&noDirect), "no direct illumination",
00265 "-no_refWait", ARG_FLAG(&refAllLinks), "don't wait for all links to be refined before stopping",
00266 "-normalise", ARG_FLAG(&normalise), "rescale scene first",
00267
00268 // cluster + FCR
00269
00270 "",
00271 "\n--- Cluster/FCR ------------------------------------------------------------\n",
00272 "-doFinal", ARG_FLAG(&bestPass), "do final 'best' pass",
00273 "-finLevels %d", &radOpts.bestLevels, " extra levels to subdivide",
00274 "-finVis", ARG_FLAG(&bestVisPass), " recalc visibility",
00275 "-noCluster", ARG_FLAG(&noClusFlag), "don't use clustering",
00276 "-scheduled", ARG_FLAG(&schedFlag), "use scheduled solver [default]",
00277 "-interleaved", ARG_FLAG(&interFlag), "use interleaved solver",
00278 "-twoStage", ARG_FLAG(&twoStageFlag), "use two-stage solver",
00279 "-schedIters %d", &radOpts.schedIterations, "max scheduled iterations to perform",
00280
00281
00282 // avars
00283
00284 "",
00285 "\n--- Avars ----------------------------------------------------------------------\n",
00286 "-set", ARG_SUBR(avarArg), "set avar, e.g. -set light 0.5 height 0.2",
00287
00288 // Output options
00289
00290 "",
00291 "\n--- Output ---------------------------------------------------------------------\n",
00292 "-slice %F", &radOpts.sliceTime, "time slice: output stats at this interval",
00293 "-limit %F", &radOpts.limitTime, "time limit: halt after this much time",
00294 "-dumpObj", ARG_FLAG(&dumpObj), "output final mesh as .obj [default]",
00295 "-dumpSL", ARG_FLAG(&dumpSL), "output final mesh as .sl",
00296 "-updateScene", ARG_FLAG(&updateScene), "update final mesh file at each slice",
00297 "-dumpScenes", ARG_FLAG(&dumpScenes), "output separate numbered mesh file for each slice",
00298 "-dumpMatrix", ARG_FLAG(&dumpMatrix), "output form-factor matrix",
00299 "-dumpHier", ARG_FLAG(&dumpTree), "output solution hierarchy",
00300 "-o %S", &radOpts.outFile, "output file name",
00301
00302
00303 // Examples
00304 "",
00305 "\n--- Examples -------------------------------------------------------------------\n",
00306 "", "radiator my-scene.sl -hprog -error 0.001 -ferr 0.01",
00307 "", "killall -HUP radiator stops simulation",
00308 "", "killall -CONT radiator print simulation status",
00309 0
00310 );
00311
00312 // Do arg parsing
00313 if (argc == 1)
00314 {
00315 fprintf(stderr, "%s\n%s\n\n%s\n\n",
00316 RadGetVersion().CString(),
00317 "(c) Andrew Willmott <ajw+rad@cs.cmu.edu> 2000",
00318 usage.CString()
00319 );
00320
00321 arg_form_print(arg_format);
00322 exit(0);
00323 }
00324 if (arg_parse_argv(argc, argv, arg_format) < 0)
00325 exit(1);
00326
00327 // Process arguments
00328
00329 if (version)
00330 {
00331 cout << RadGetVersion() << endl;
00332 cout << "Built on " << kBuildDate << endl;
00333 cout << "A radiosity engine, (c) Andrew Willmott <ajw+rad@cs.cmu.edu> 1999" << endl;
00334 exit(0);
00335 }
00336
00337 if (!sceneFile)
00338 {
00339 cout << "No scene file specified!" << endl;
00340 exit(1);
00341 }
00342
00343 radOpts.cluster = !noClusFlag;
00344 radOpts.bestPass = bestPass;
00345 radOpts.bestVisPass = bestVisPass;
00346 radOpts.dumpScenes = dumpScenes;
00347 radOpts.updateScene = updateScene;
00348 radOpts.dumpTree = dumpTree;
00349 radOpts.drawMatrix = dumpMatrix;
00350
00351 if (!dumpSL && !dumpScenes)
00352 dumpObj = true;
00353
00354
00355 if (ana)
00356 radOpts.method = kAnalytical;
00357 else if (hier)
00358 radOpts.method = kHierarchical;
00359 else if (hprog)
00360 radOpts.method = kProgSubstruct;
00361 else if (prog)
00362 radOpts.method = kProgressive;
00363 else if (mat)
00364 radOpts.method = kMatrix;
00365 else
00366 // default to progressive radiosity with substructuring
00367 radOpts.method = kProgSubstruct;
00368
00369 if (vis44)
00370 radOpts.visibility = vis_4x4;
00371 else if (vis16)
00372 radOpts.visibility = vis_16x1;
00373 else if (vis1)
00374 radOpts.visibility = vis_1;
00375 else if (visnone)
00376 radOpts.visibility = vis_none;
00377 else
00378 radOpts.visibility = vis_4x4;
00379
00380 if (meshRnd)
00381 radOpts.mesh = mesh_random;
00382 if (meshNL)
00383 radOpts.mesh = mesh_nonlin;
00384 radOpts.fixMesh = meshFix;
00385 radOpts.connectMesh = !meshNoConnect;
00386 radOpts.noGridMesh = meshNoGrid;
00387
00388 if (!radOpts.outFile)
00389 // make up a name for the output file
00390 {
00391 FileName inFile;
00392
00393 inFile.SetPath(sceneFile);
00394
00395 if (fcrFlag)
00396 outFile = "fcr";
00397 else if (hier)
00398 outFile = defBasisName[radOpts.basis];
00399 else
00400 outFile = defMethodName[radOpts.method];
00401
00402 outFile = outFile + "-" + inFile.GetFile();
00403 }
00404 else
00405 outFile = radOpts.outFile;
00406
00407 if (frame >= 0)
00408 // add a frame number
00409 outFile = outFile + String().Printf("-%04d", frame);
00410
00411 radOpts.outFile = outFile;
00412 // XXX make optional?
00413 if (true)
00414 LogToFile(FileName().SetPath(outFile).SetExtension("run"));
00415
00416
00417 if (interFlag)
00418 radOpts.solver = sv_interleaved;
00419 else if (twoStageFlag)
00420 radOpts.solver = sv_twoStage;
00421 else
00422 radOpts.solver = sv_scheduled;
00423
00424 radOpts.useConjGrad = conjGrad;
00425 radOpts.ambient = ambient;
00426 radOpts.graded = !noGraded;
00427 radOpts.anchor = !noAnchor;
00428 radOpts.refAllLinks = !refAllLinks;
00429 radOpts.visInQuad = viq;
00430
00431 radOpts.meshComplexity = meshComp;
00432 if (rtComp >= 0.0)
00433 radOpts.rtComplexity = rtComp;
00434 else
00435 radOpts.rtComplexity = meshComp;
00436 }
00437
00438
00439 Void RadMain::DoRadiosity()
00440 {
00441 Int i;
00442 scScenePtr scene;
00443 RadMethod *radMethod = 0;
00444 CreateAvarList makeAvarList;
00445 FileName path;
00446
00447 cout << RadGetVersion() << endl;
00448 slInit();
00449 gRadControl = &radOpts;
00450
00451 path.SetPath(sceneFile);
00452 scene = SceneReader::Load(path);
00453
00454 if (!scene)
00455 {
00456 cerr << "Couldn't read file: " << sceneFile << endl;
00457 exit(1);
00458 }
00459
00460 // Set avars...
00461
00462
00463 scene->ApplyAction(makeAvarList);
00464
00465 #ifdef DEBUG
00466 cout << "set avars:" << endl;
00467 for (i = 0; i < gAvarNames.NumItems(); i++)
00468 cout << "avar " << gAvarNames[i] << " = " << gAvarVals[i] << endl;
00469 cout << "scene avars:" << endl;
00470 for (i = 0; i < makeAvarList.avarList->NumItems(); i++)
00471 cout << makeAvarList.avarList->Item(i).name << " = "
00472 << makeAvarList.avarList->Item(i).value << endl;
00473 #endif
00474
00475 scene->Set(makeAvarList.avarList);
00476
00477 for (i = 0; i < gAvarNames.NumItems(); i++)
00478 if (!makeAvarList.avarList->SetAvar(gAvarNames[i], gAvarVals[i]))
00479 cerr << "No such avar in scene: " << gAvarNames[i]
00480 << " (ignoring) " << endl;
00481
00482 // Create radiosity method
00483
00484 radMethod = RadMethod::NewRadMethod();
00485
00486 // Run radiosity method...
00487
00488 if (radMethod)
00489 {
00490 if (normalise)
00491 scene->Normalise();
00492 radMethod->SetScene(scene);
00493 radMethod->Render();
00494 if (noDirect)
00495 radMethod->RemoveDirect();
00496
00497 // Output scene file
00498
00499 if (dumpObj)
00500 {
00501 cerr << "Saving final mesh as " + radOpts.outFile + ".obj" << endl;
00502 radMethod->WriteObjFile(radOpts.outFile + ".obj");
00503 }
00504 if (dumpSL)
00505 {
00506 cerr << "Saving final mesh as " + radOpts.outFile + ".sl" << endl;
00507 radMethod->WriteSLFile(radOpts.outFile + ".sl");
00508 }
00509 }
00510 else
00511 cerr << "Sorry, that method has not been compiled in!" << endl;
00512 }
00513
00514 main(int argc, char **argv)
00515 {
00516 RadMain app;
00517
00518 #ifdef CL_HAS_NEW
00519 try
00520 {
00521 #endif
00522 app.SetOptions(argc, argv);
00523 signal(SIGHUP, RadHandleInterrupt);
00524 signal(SIGCONT, RadHandleCont);
00525 app.DoRadiosity();
00526 #ifdef CL_HAS_NEW
00527 }
00528 catch (bad_alloc)
00529 {
00530 cout << "EXCEPTION: out of (virtual) memory." << endl;
00531 return(-1);
00532 }
00533 #endif
00534
00535 return(0);
00536 }