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 }