| 1 | #ifndef lint | 
| 2 | static const char       RCSid[] = "$Id: renderopts.c,v 2.27 2025/04/22 17:12:25 greg Exp $"; | 
| 3 | #endif | 
| 4 | /* | 
| 5 | *  renderopts.c - process common rendering options | 
| 6 | * | 
| 7 | *  External symbols declared in ray.h | 
| 8 | */ | 
| 9 |  | 
| 10 | #include "copyright.h" | 
| 11 |  | 
| 12 | #include  "ray.h" | 
| 13 | #include  "func.h" | 
| 14 | #include  "paths.h" | 
| 15 | #include  "pmapopt.h" | 
| 16 |  | 
| 17 | extern char     *progname;      /* global argv[0] */ | 
| 18 |  | 
| 19 | char    RFeatureList[2048] =    /* newline-separated feature list */ | 
| 20 | "VirtualSources\nSecondarySources\nSourceSubsampling\n" | 
| 21 | "SourceVisibility\nAmbientModifierSelection\n" | 
| 22 | "PathTracing\nRussianRoulette\nLowDiscrepancySeq\n" | 
| 23 | "SpecularSampling\nMaterialMixtures\nAntimatter\nBackFaceVisibility\n" | 
| 24 | "ScatteringModels=WGMD,Ashikhmin-Shirley\n" | 
| 25 | "TabulatedBSDFs=DataFile,KlemsXML,TensorTreeXML,+ViewPeakExtraction\n" | 
| 26 | "Instancing=Octree,TriangleMesh\nAliases\n" | 
| 27 | #if MAXCSAMP>3 | 
| 28 | "Hyperspectral\n" | 
| 29 | #endif | 
| 30 | #if !defined(SHADCACHE) || SHADCACHE > 0 | 
| 31 | "ShadowCache\n" | 
| 32 | #endif | 
| 33 | #ifdef  DISPERSE | 
| 34 | "DielectricDispersion\n" | 
| 35 | #endif | 
| 36 | /*              PMAP_FEATURES   XXX @Roland: need to define this in pmapopt.h */ | 
| 37 | ; | 
| 38 |  | 
| 39 |  | 
| 40 | static char * | 
| 41 | get_feature(            /* find a specific feature (with optional sublist) */ | 
| 42 | const char *feat | 
| 43 | ) | 
| 44 | { | 
| 45 | char    *cp = RFeatureList; | 
| 46 | int     n = 0; | 
| 47 |  | 
| 48 | while ((feat[n] != '\0') & (feat[n] != '=')) | 
| 49 | n++; | 
| 50 | if (!n) | 
| 51 | return(NULL); | 
| 52 | while (*cp) { | 
| 53 | if (!strncmp(cp, feat, n) && (cp[n] == '\n') | !feat[n] | (cp[n] == feat[n])) | 
| 54 | return(cp); | 
| 55 | while (*cp++ != '\n') | 
| 56 | ; | 
| 57 | } | 
| 58 | return(NULL); | 
| 59 | } | 
| 60 |  | 
| 61 |  | 
| 62 | static int | 
| 63 | match_subfeatures(      /* check if subfeatures are supported */ | 
| 64 | char *mysublist, | 
| 65 | char *reqs | 
| 66 | ) | 
| 67 | { | 
| 68 | if (mysublist) | 
| 69 | mysublist = strchr(mysublist, '='); | 
| 70 | if (!mysublist++ | !reqs) | 
| 71 | return(0);              /* not a feature list */ | 
| 72 | while (*reqs) {                 /* check each of their subfeature requests */ | 
| 73 | char    subfeat[64]; | 
| 74 | char    *cp = subfeat; | 
| 75 | int     n; | 
| 76 | while (*reqs && (*cp = *reqs++) != ',') | 
| 77 | cp++; | 
| 78 | *cp = '\0'; | 
| 79 | if (!(n = cp - subfeat)) | 
| 80 | continue;       /* empty subfeature */ | 
| 81 | for (cp = mysublist; (cp = strstr(cp, subfeat)) != NULL; cp++) | 
| 82 | if ((cp[-1] == ',') | (cp[-1] == '=') && | 
| 83 | (cp[n] == ',') | (cp[n] == '\n')) | 
| 84 | break;  /* match */ | 
| 85 | if (!cp) | 
| 86 | return(0);      /* missing this one! */ | 
| 87 | } | 
| 88 | return(1);                      /* matched them all */ | 
| 89 | } | 
| 90 |  | 
| 91 |  | 
| 92 | int | 
| 93 | feature_status(         /* report active feature list / check specifics */ | 
| 94 | int  ac, | 
| 95 | char  *av[] | 
| 96 | ) | 
| 97 | { | 
| 98 | if (ac <= 0)                    /* report entire list? */ | 
| 99 | fputs(RFeatureList, stdout); | 
| 100 |  | 
| 101 | for ( ; ac-- > 0; av++) {       /* check each argument */ | 
| 102 | char    *cp; | 
| 103 | if (!*av[0]) continue; | 
| 104 | if ((cp = strchr(av[0], '=')) != NULL) { | 
| 105 | if (!match_subfeatures(get_feature(av[0]), cp+1)) | 
| 106 | goto missing_feature; | 
| 107 | } else if ((cp = get_feature(av[0])) != NULL) { | 
| 108 | char    *tp = strchr(cp, '='); | 
| 109 | if (tp && tp < strchr(cp, '\n')) | 
| 110 | do | 
| 111 | fputc(*cp, stdout); | 
| 112 | while (*cp++ != '\n'); | 
| 113 | } else | 
| 114 | goto missing_feature; | 
| 115 | } | 
| 116 | return(0);                      /* return satisfactory status */ | 
| 117 | missing_feature:                        /* or report error */ | 
| 118 | fprintf(stderr, "%s: missing feature - %s\n", progname, av[0]); | 
| 119 | return(1); | 
| 120 | } | 
| 121 |  | 
| 122 |  | 
| 123 | int | 
| 124 | getrenderopt(           /* get next render option */ | 
| 125 | int  ac, | 
| 126 | char  *av[] | 
| 127 | ) | 
| 128 | { | 
| 129 | #define  check(ol,al)           if (av[0][ol] || \ | 
| 130 | badarg(ac-1,av+1,al)) \ | 
| 131 | return(-1) | 
| 132 | #define  check_bool(olen,var)           switch (av[0][olen]) { \ | 
| 133 | case '\0': var = !var; break; \ | 
| 134 | case 'y': case 'Y': case 't': case 'T': \ | 
| 135 | case '+': case '1': var = 1; break; \ | 
| 136 | case 'n': case 'N': case 'f': case 'F': \ | 
| 137 | case '-': case '0': var = 0; break; \ | 
| 138 | default: return(-1); } | 
| 139 | static char  **amblp;           /* pointer to build ambient list */ | 
| 140 | int     rval; | 
| 141 | /* is it even an option? */ | 
| 142 | if (ac < 1 || av[0] == NULL || av[0][0] != '-') | 
| 143 | return(-1); | 
| 144 | /* check if it's one we know */ | 
| 145 | switch (av[0][1]) { | 
| 146 | case 'u':                               /* uncorrelated sampling */ | 
| 147 | check_bool(2,rand_samp); | 
| 148 | return(0); | 
| 149 | case 'b':                               /* back face vis. */ | 
| 150 | if (av[0][2] == 'v') { | 
| 151 | check_bool(3,backvis); | 
| 152 | return(0); | 
| 153 | } | 
| 154 | break; | 
| 155 | case 'd':                               /* direct */ | 
| 156 | switch (av[0][2]) { | 
| 157 | case 't':                               /* threshold */ | 
| 158 | check(3,"f"); | 
| 159 | shadthresh = atof(av[1]); | 
| 160 | return(1); | 
| 161 | case 'c':                               /* certainty */ | 
| 162 | check(3,"f"); | 
| 163 | shadcert = atof(av[1]); | 
| 164 | return(1); | 
| 165 | case 'j':                               /* jitter */ | 
| 166 | check(3,"f"); | 
| 167 | dstrsrc = atof(av[1]); | 
| 168 | return(1); | 
| 169 | case 'r':                               /* relays */ | 
| 170 | check(3,"i"); | 
| 171 | directrelay = atoi(av[1]); | 
| 172 | return(1); | 
| 173 | case 'p':                               /* pretest */ | 
| 174 | check(3,"i"); | 
| 175 | vspretest = atoi(av[1]); | 
| 176 | return(1); | 
| 177 | case 'v':                               /* visibility */ | 
| 178 | check_bool(3,directvis); | 
| 179 | return(0); | 
| 180 | case 's':                               /* size */ | 
| 181 | check(3,"f"); | 
| 182 | srcsizerat = atof(av[1]); | 
| 183 | return(1); | 
| 184 | } | 
| 185 | break; | 
| 186 | case 's':                               /* specular */ | 
| 187 | switch (av[0][2]) { | 
| 188 | case 't':                               /* threshold */ | 
| 189 | check(3,"f"); | 
| 190 | specthresh = atof(av[1]); | 
| 191 | return(1); | 
| 192 | case 's':                               /* sampling */ | 
| 193 | check(3,"f"); | 
| 194 | specjitter = atof(av[1]); | 
| 195 | return(1); | 
| 196 | } | 
| 197 | break; | 
| 198 | case 'l':                               /* limit */ | 
| 199 | switch (av[0][2]) { | 
| 200 | case 'r':                               /* recursion */ | 
| 201 | check(3,"i"); | 
| 202 | maxdepth = atoi(av[1]); | 
| 203 | return(1); | 
| 204 | case 'w':                               /* weight */ | 
| 205 | check(3,"f"); | 
| 206 | minweight = atof(av[1]); | 
| 207 | return(1); | 
| 208 | } | 
| 209 | break; | 
| 210 | case 'i':                               /* irradiance */ | 
| 211 | check_bool(2,do_irrad); | 
| 212 | return(0); | 
| 213 | case 'a':                               /* ambient */ | 
| 214 | switch (av[0][2]) { | 
| 215 | case 'v':                               /* value */ | 
| 216 | check(3,"fff"); | 
| 217 | setcolor(ambval, atof(av[1]), | 
| 218 | atof(av[2]), | 
| 219 | atof(av[3])); | 
| 220 | return(3); | 
| 221 | case 'w':                               /* weight */ | 
| 222 | check(3,"i"); | 
| 223 | ambvwt = atoi(av[1]); | 
| 224 | return(1); | 
| 225 | case 'a':                               /* accuracy */ | 
| 226 | check(3,"f"); | 
| 227 | ambacc = atof(av[1]); | 
| 228 | return(1); | 
| 229 | case 'r':                               /* resolution */ | 
| 230 | check(3,"i"); | 
| 231 | ambres = atoi(av[1]); | 
| 232 | return(1); | 
| 233 | case 'd':                               /* divisions */ | 
| 234 | check(3,"i"); | 
| 235 | ambdiv = atoi(av[1]); | 
| 236 | return(1); | 
| 237 | case 's':                               /* super-samp */ | 
| 238 | check(3,"i"); | 
| 239 | ambssamp = atoi(av[1]); | 
| 240 | return(1); | 
| 241 | case 'b':                               /* bounces */ | 
| 242 | check(3,"i"); | 
| 243 | ambounce = atoi(av[1]); | 
| 244 | return(1); | 
| 245 | case 'i':                               /* include */ | 
| 246 | case 'I': | 
| 247 | check(3,"s"); | 
| 248 | if (ambincl != 1) { | 
| 249 | ambincl = 1; | 
| 250 | amblp = amblist; | 
| 251 | } | 
| 252 | if (av[0][2] == 'I') {  /* file */ | 
| 253 | rval = wordfile(amblp, AMBLLEN-(amblp-amblist), | 
| 254 | getpath(av[1],getrlibpath(),R_OK)); | 
| 255 | if (rval < 0) { | 
| 256 | sprintf(errmsg, | 
| 257 | "cannot open ambient include file \"%s\"", av[1]); | 
| 258 | error(SYSTEM, errmsg); | 
| 259 | } | 
| 260 | amblp += rval; | 
| 261 | } else { | 
| 262 | *amblp++ = savqstr(av[1]); | 
| 263 | *amblp = NULL; | 
| 264 | } | 
| 265 | return(1); | 
| 266 | case 'e':                               /* exclude */ | 
| 267 | case 'E': | 
| 268 | check(3,"s"); | 
| 269 | if (ambincl != 0) { | 
| 270 | ambincl = 0; | 
| 271 | amblp = amblist; | 
| 272 | } | 
| 273 | if (av[0][2] == 'E') {  /* file */ | 
| 274 | rval = wordfile(amblp, AMBLLEN-(amblp-amblist), | 
| 275 | getpath(av[1],getrlibpath(),R_OK)); | 
| 276 | if (rval < 0) { | 
| 277 | sprintf(errmsg, | 
| 278 | "cannot open ambient exclude file \"%s\"", av[1]); | 
| 279 | error(SYSTEM, errmsg); | 
| 280 | } | 
| 281 | amblp += rval; | 
| 282 | } else { | 
| 283 | *amblp++ = savqstr(av[1]); | 
| 284 | *amblp = NULL; | 
| 285 | } | 
| 286 | return(1); | 
| 287 | case 'f':                               /* file */ | 
| 288 | check(3,"s"); | 
| 289 | ambfile = savqstr(av[1]); | 
| 290 | return(1); | 
| 291 | } | 
| 292 | break; | 
| 293 | case 'm':                               /* medium */ | 
| 294 | switch (av[0][2]) { | 
| 295 | case 'e':                               /* extinction */ | 
| 296 | check(3,"fff"); | 
| 297 | setcolor(cextinction, atof(av[1]), | 
| 298 | atof(av[2]), | 
| 299 | atof(av[3])); | 
| 300 | return(3); | 
| 301 | case 'a':                               /* albedo */ | 
| 302 | check(3,"fff"); | 
| 303 | setcolor(salbedo, atof(av[1]), | 
| 304 | atof(av[2]), | 
| 305 | atof(av[3])); | 
| 306 | return(3); | 
| 307 | case 'g':                               /* eccentr. */ | 
| 308 | check(3,"f"); | 
| 309 | seccg = atof(av[1]); | 
| 310 | return(1); | 
| 311 | case 's':                               /* sampling */ | 
| 312 | check(3,"f"); | 
| 313 | ssampdist = atof(av[1]); | 
| 314 | return(1); | 
| 315 | } | 
| 316 | break; | 
| 317 | case 'f':                               /* .cal file */ | 
| 318 | check(2,"s"); | 
| 319 | loadfunc(av[1]); | 
| 320 | return(1); | 
| 321 | case 'e':                               /* .cal expression */ | 
| 322 | check(2,"s"); | 
| 323 | if (!strchr(av[1], '=') && !strchr(av[1], ':')) | 
| 324 | break; | 
| 325 | scompile(av[1], NULL, 0); | 
| 326 | return(1); | 
| 327 | #if MAXCSAMP>3 | 
| 328 | case 'c':                               /* spectral sampling */ | 
| 329 | switch (av[0][2]) { | 
| 330 | case 's':                       /* spectral bin count */ | 
| 331 | check(3,"i"); | 
| 332 | NCSAMP = atoi(av[1]); | 
| 333 | return(1); | 
| 334 | case 'w':                       /* wavelength extrema */ | 
| 335 | check(3,"ff"); | 
| 336 | WLPART[0] = atof(av[1]); | 
| 337 | WLPART[3] = atof(av[2]); | 
| 338 | return(2); | 
| 339 | } | 
| 340 | break; | 
| 341 | #endif | 
| 342 | } | 
| 343 |  | 
| 344 | /* PMAP: Parse photon mapping options */ | 
| 345 | return(getPmapRenderOpt(ac, av)); | 
| 346 |  | 
| 347 | /*      return(-1); */          /* unknown option */ | 
| 348 |  | 
| 349 | #undef  check | 
| 350 | #undef  check_bool | 
| 351 | } | 
| 352 |  | 
| 353 |  | 
| 354 | void | 
| 355 | print_rdefaults(void)           /* print default render values to stdout */ | 
| 356 | { | 
| 357 | printf(do_irrad ? "-i+\t\t\t\t# irradiance calculation on\n" : | 
| 358 | "-i-\t\t\t\t# irradiance calculation off\n"); | 
| 359 | printf(rand_samp ? "-u+\t\t\t\t# uncorrelated Monte Carlo sampling\n" : | 
| 360 | "-u-\t\t\t\t# correlated quasi-Monte Carlo sampling\n"); | 
| 361 | printf(backvis ? "-bv+\t\t\t\t# back face visibility on\n" : | 
| 362 | "-bv-\t\t\t\t# back face visibility off\n"); | 
| 363 | printf("-dt %f\t\t\t# direct threshold\n", shadthresh); | 
| 364 | printf("-dc %f\t\t\t# direct certainty\n", shadcert); | 
| 365 | printf("-dj %f\t\t\t# direct jitter\n", dstrsrc); | 
| 366 | printf("-ds %f\t\t\t# direct sampling\n", srcsizerat); | 
| 367 | printf("-dr %-9d\t\t\t# direct relays\n", directrelay); | 
| 368 | printf("-dp %-9d\t\t\t# direct pretest density\n", vspretest); | 
| 369 | printf(directvis ? "-dv+\t\t\t\t# direct visibility on\n" : | 
| 370 | "-dv-\t\t\t\t# direct visibility off\n"); | 
| 371 | printf("-ss %f\t\t\t# specular sampling\n", specjitter); | 
| 372 | printf("-st %f\t\t\t# specular threshold\n", specthresh); | 
| 373 | printf("-av %f %f %f\t# ambient value\n", colval(ambval,RED), | 
| 374 | colval(ambval,GRN), colval(ambval, BLU)); | 
| 375 | printf("-aw %-9d\t\t\t# ambient value weight\n", ambvwt); | 
| 376 | printf("-ab %-9d\t\t\t# ambient bounces\n", ambounce); | 
| 377 | printf("-aa %f\t\t\t# ambient accuracy\n", ambacc); | 
| 378 | printf("-ar %-9d\t\t\t# ambient resolution\n", ambres); | 
| 379 | printf("-ad %-9d\t\t\t# ambient divisions\n", ambdiv); | 
| 380 | printf("-as %-9d\t\t\t# ambient super-samples\n", ambssamp); | 
| 381 | printf("-me %.2e %.2e %.2e\t# mist extinction coefficient\n", | 
| 382 | colval(cextinction,RED), | 
| 383 | colval(cextinction,GRN), | 
| 384 | colval(cextinction,BLU)); | 
| 385 | printf("-ma %f %f %f\t# mist scattering albedo\n", colval(salbedo,RED), | 
| 386 | colval(salbedo,GRN), colval(salbedo,BLU)); | 
| 387 | printf("-mg %f\t\t\t# mist scattering eccentricity\n", seccg); | 
| 388 | printf("-ms %f\t\t\t# mist sampling distance\n", ssampdist); | 
| 389 | if (NCSAMP > 3) { | 
| 390 | printf("-cs %-2d\t\t\t\t# number of spectral bins\n", NCSAMP); | 
| 391 | printf("-cw %3.0f %3.0f\t\t\t# wavelength limits (nm)\n", | 
| 392 | WLPART[3], WLPART[0]); | 
| 393 | } | 
| 394 | printf("-lr %-9d\t\t\t# limit reflection%s\n", maxdepth, | 
| 395 | maxdepth<=0 ? " (Russian roulette)" : ""); | 
| 396 | printf("-lw %.2e\t\t\t# limit weight\n", minweight); | 
| 397 |  | 
| 398 | /* PMAP: output photon map defaults */ | 
| 399 | printPmapDefaults(); | 
| 400 | } |