1 /*
2 Teem: Tools to process and visualize scientific data and images .
3 Copyright (C) 2012, 2011, 2010, 2009 University of Chicago
4 Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann
5 Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public License
9 (LGPL) as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11 The terms of redistributing and/or modifying this software also
12 include exceptions to the LGPL that facilitate static linking.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with this library; if not, write to Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include "ten.h"
25 #include "privateTen.h"
26
27 #define INFO "Generate postscript or ray-traced renderings of 3D glyphs"
28 static const char *_tend_glyphInfoL =
29 (INFO
30 ". Whether the output is postscript or a ray-traced image is controlled "
31 "by the initial \"rt\" flag (by default, the output is postscript). "
32 "Because this is doing viz/graphics, many parameters need to be set. "
33 "Use a response file to simplify giving the command-line options which "
34 "aren't changing between invocations. "
35 "The postscript output is an EPS file, suitable for including as a figure "
36 "in LaTeX, or viewing with ghostview, or distilling into PDF. "
37 "The ray-traced output is a 5 channel (R,G,B,A,T) float nrrd, suitable for "
38 "\"unu crop -min 0 0 0 -max 2 M M \" followed by "
39 "\"unu gamma\" and/or \"unu quantize -b 8\".");
40
41 #define _LIMNMAGIC "LIMN0000"
42
43 int
_tendGlyphReadCams(int imgSize[2],limnCamera ** camP,unsigned int * numCamsP,FILE * fin)44 _tendGlyphReadCams(int imgSize[2], limnCamera **camP,
45 unsigned int *numCamsP, FILE *fin) {
46 static const char me[]="_tendGlyphReadCams";
47 char line[AIR_STRLEN_HUGE];
48 int ki;
49 double di, dn, df, fr[3], at[3], up[3], va, dwell;
50 airArray *mop, *camA;
51
52 if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE)
53 && !strcmp(_LIMNMAGIC, line) )) {
54 biffAddf(TEN, "%s: couldn't read first line or it wasn't \"%s\"",
55 me, _LIMNMAGIC);
56 return 1;
57 }
58 if (!( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE)
59 && 2 == (airStrtrans(airStrtrans(line, '{', ' '), '}', ' '),
60 sscanf(line, "imgSize %d %d", imgSize+0, imgSize+1)) )) {
61 biffAddf(TEN, "%s: couldn't read second line or it wasn't "
62 "\"imgSize <sizeX> <sizeY>\"", me);
63 return 1;
64 }
65
66 mop = airMopNew();
67 camA = airArrayNew((void **)camP, numCamsP, sizeof(limnCamera), 1);
68 airMopAdd(mop, camA, (airMopper)airArrayNix, airMopAlways);
69
70 while ( 0 < airOneLine(fin, line, AIR_STRLEN_HUGE) ) {
71 airStrtrans(airStrtrans(line, '{', ' '), '}', ' ');
72 ki = airArrayLenIncr(camA, 1);
73 if (14 != sscanf(line, "cam.di %lg cam.at %lg %lg %lg "
74 "cam.up %lg %lg %lg cam.dn %lg cam.df %lg cam.va %lg "
75 "relDwell %lg cam.fr %lg %lg %lg",
76 &di, at+0, at+1, at+2,
77 up+0, up+1, up+2, &dn, &df, &va,
78 &dwell, fr+0, fr+1, fr+2)) {
79 biffAddf(TEN, "%s: trouble parsing line %d: \"%s\"", me, ki, line);
80 airMopError(mop); return 1;
81 }
82 (*camP)[ki].neer = dn;
83 (*camP)[ki].faar = df;
84 (*camP)[ki].dist = di;
85 ELL_3V_COPY((*camP)[ki].from, fr);
86 ELL_3V_COPY((*camP)[ki].at, at);
87 ELL_3V_COPY((*camP)[ki].up, up);
88 (*camP)[ki].fov = va;
89 (*camP)[ki].aspect = (double)imgSize[0]/imgSize[1];
90 (*camP)[ki].atRelative = AIR_FALSE;
91 (*camP)[ki].orthographic = AIR_FALSE;
92 (*camP)[ki].rightHanded = AIR_TRUE;
93 }
94
95 airMopOkay(mop);
96 return 0;
97 }
98
99 int
tend_glyphMain(int argc,const char ** argv,const char * me,hestParm * hparm)100 tend_glyphMain(int argc, const char **argv, const char *me,
101 hestParm *hparm) {
102 int pret, doRT = AIR_FALSE;
103 hestOpt *hopt = NULL;
104 char *perr, *err;
105 airArray *mop;
106
107 Nrrd *nin, *emap, *nraw, *npos, *nslc;
108 char *outS;
109 limnCamera *cam, *hackcams;
110 limnObject *glyph;
111 limnWindow *win;
112 echoObject *rect=NULL;
113 echoScene *scene;
114 echoRTParm *eparm;
115 echoGlobalState *gstate;
116 tenGlyphParm *gparm;
117 float bg[3], edgeColor[3], buvne[5], shadow, creaseAngle;
118 int ires[2], slice[2], nobg, ambocc, concave;
119 unsigned int hackci, hacknumcam;
120 size_t hackmin[3]={0,0,0}, hackmax[3]={2,0,0};
121 char *hackFN, hackoutFN[AIR_STRLEN_SMALL];
122 FILE *hackF;
123 Nrrd *hacknpng, *hacknrgb;
124 NrrdRange *hackrange;
125
126 double v2w[9], ldir[3], edir[3], fdir[3], corn[3], len;
127
128 /* so that command-line options can be read from file */
129 hparm->respFileEnable = AIR_TRUE;
130 hparm->elideSingleEmptyStringDefault = AIR_TRUE;
131
132 mop = airMopNew();
133 cam = limnCameraNew();
134 airMopAdd(mop, cam, (airMopper)limnCameraNix, airMopAlways);
135 glyph = limnObjectNew(1000, AIR_TRUE);
136 airMopAdd(mop, glyph, (airMopper)limnObjectNix, airMopAlways);
137 scene = echoSceneNew();
138 airMopAdd(mop, scene, (airMopper)echoSceneNix, airMopAlways);
139 win = limnWindowNew(limnDevicePS);
140 airMopAdd(mop, win, (airMopper)limnWindowNix, airMopAlways);
141 gparm = tenGlyphParmNew();
142 airMopAdd(mop, gparm, (airMopper)tenGlyphParmNix, airMopAlways);
143 eparm = echoRTParmNew();
144 airMopAdd(mop, eparm, (airMopper)echoRTParmNix, airMopAlways);
145
146 /* do postscript or ray-traced? */
147 hestOptAdd(&hopt, "rt", NULL, airTypeFloat, 0, 0, &doRT, NULL,
148 "generate ray-traced output. By default (not using this "
149 "option), postscript output is generated.");
150
151 hestOptAdd(&hopt, "v", "level", airTypeInt, 1, 1, &(gparm->verbose), "0",
152 "verbosity level");
153
154 /* which points will rendered */
155 hestOptAdd(&hopt, "ctr", "conf thresh", airTypeFloat, 1, 1,
156 &(gparm->confThresh), "0.5",
157 "Glyphs will be drawn only for tensors with confidence "
158 "values greater than this threshold");
159 hestOptAdd(&hopt, "a", "aniso", airTypeEnum, 1, 1,
160 &(gparm->anisoType), "fa",
161 "Which anisotropy metric to use for thresholding the data "
162 "points to be drawn", NULL, tenAniso);
163 hestOptAdd(&hopt, "atr", "aniso thresh", airTypeFloat, 1, 1,
164 &(gparm->anisoThresh), "0.5",
165 "Glyphs will be drawn only for tensors with anisotropy "
166 "greater than this threshold");
167 hestOptAdd(&hopt, "p", "pos array", airTypeOther, 1, 1, &npos, "",
168 "Instead of being on a grid, tensors are at arbitrary locations, "
169 "as defined by this 3-by-N array of floats. Doing this makes "
170 "various other options moot", NULL, NULL,
171 nrrdHestNrrd);
172 hestOptAdd(&hopt, "m", "mask vol", airTypeOther, 1, 1, &(gparm->nmask), "",
173 "Scalar volume (if any) for masking region in which glyphs are "
174 "drawn, in conjunction with \"mtr\" flag. ", NULL, NULL,
175 nrrdHestNrrd);
176 hestOptAdd(&hopt, "mtr", "mask thresh", airTypeFloat, 1, 1,
177 &(gparm->maskThresh),
178 "0.5", "Glyphs will be drawn only for tensors with mask "
179 "value greater than this threshold");
180
181 /* how glyphs will be shaped */
182 hestOptAdd(&hopt, "g", "glyph shape", airTypeEnum, 1, 1,
183 &(gparm->glyphType), "box",
184 "shape of glyph to use for display. Possibilities "
185 "include \"box\", \"sphere\", \"cylinder\", and "
186 "\"superquad\"", NULL, tenGlyphType);
187 hestOptAdd(&hopt, "sh", "sharpness", airTypeFloat, 1, 1,
188 &(gparm->sqdSharp), "3.0",
189 "for superquadric glyphs, how much to sharp edges form as a "
190 "function of differences between eigenvalues. Higher values "
191 "mean that edges form more easily");
192 hestOptAdd(&hopt, "gsc", "scale", airTypeFloat, 1, 1, &(gparm->glyphScale),
193 "0.01", "over-all glyph size in world-space");
194
195 /* how glyphs will be colored */
196 hestOptAdd(&hopt, "c", "evector #", airTypeInt, 1, 1, &(gparm->colEvec), "0",
197 "which eigenvector should determine coloring. "
198 "(formally \"v\") "
199 "\"0\", \"1\", \"2\" are principal, medium, and minor");
200 hestOptAdd(&hopt, "sat", "saturation", airTypeFloat, 1, 1,
201 &(gparm->colMaxSat), "1.0",
202 "maximal saturation to use on glyph colors (use 0.0 to "
203 "create a black and white image)");
204 hestOptAdd(&hopt, "ga", "aniso", airTypeEnum, 1, 1,
205 &(gparm->colAnisoType), "fa",
206 "Which anisotropy metric to use for modulating the "
207 "saturation of the glyph color", NULL, tenAniso);
208 hestOptAdd(&hopt, "am", "aniso mod", airTypeFloat, 1, 1,
209 &(gparm->colAnisoModulate),
210 "0.0", "How much to modulate glyph color saturation by "
211 "anisotropy (as chosen by \"-ga\"). "
212 "If 1.0, then glyphs for zero anisotropy "
213 "data points will have no hue. ");
214 hestOptAdd(&hopt, "gg", "gray", airTypeFloat, 1, 1, &(gparm->colIsoGray),
215 "1.0", "desaturating glyph color due to low anisotropy "
216 "tends towards this gray level");
217 hestOptAdd(&hopt, "gam", "gamma", airTypeFloat, 1, 1, &(gparm->colGamma),
218 "0.7", "gamma to use on color components (after saturation)");
219 hestOptAdd(&hopt, "emap", "env map", airTypeOther, 1, 1, &emap, "",
220 "environment map to use for shading glyphs. By default, "
221 "there is no shading", NULL, NULL, nrrdHestNrrd);
222 hestOptAdd(&hopt, "adsp", "phong", airTypeFloat, 4, 4, &(gparm->ADSP),
223 "0 1 0 30", "phong ambient, diffuse, specular components, "
224 "and specular power");
225 hestOptAdd(&hopt, "bg", "background", airTypeFloat, 3, 3, bg, "1 1 1",
226 "background RGB color; each component in range [0.0,1.0]");
227 hestOptAdd(&hopt, "ec", "edge rgb", airTypeFloat, 3, 3, edgeColor, "0 0 0",
228 "edge RGB color; each component in range [0.0,1.0]");
229
230 /* parameters for showing a dataset slice */
231 hestOptAdd(&hopt, "slc", "axis pos", airTypeInt, 2, 2, slice, "-1 -1",
232 "For showing a gray-scale slice of anisotropy: the axis "
233 "and position along which to slice. Use \"-1 -1\" to signify "
234 "that no slice should be shown");
235 hestOptAdd(&hopt, "si", "slice image", airTypeOther, 1, 1, &nslc, "",
236 "Instead of showing a slice of the anisotropy used to cull "
237 "glyphs, show something else. ", NULL, NULL,
238 nrrdHestNrrd);
239 hestOptAdd(&hopt, "off", "slice offset", airTypeFloat, 1, 1,
240 &(gparm->sliceOffset), "0.0",
241 "Offset from slice position to render slice at (so that it "
242 "doesn't occlude glyphs).");
243 hestOptAdd(&hopt, "sg", "slice gamma", airTypeFloat, 1, 1,
244 &(gparm->sliceGamma), "1.7",
245 "Gamma to apply to values on slice.");
246 hestOptAdd(&hopt, "sb", "slice bias", airTypeFloat, 1, 1,
247 &(gparm->sliceBias), "0.05",
248 "amount by which to bump up slice gray values prior to gamma.");
249
250 /* camera */
251 hestOptAdd(&hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from, NULL,
252 "position of camera, used to determine view vector");
253 hestOptAdd(&hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0",
254 "camera look-at point, used to determine view vector");
255 hestOptAdd(&hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1",
256 "camera pseudo-up vector, used to determine view coordinates");
257 hestOptAdd(&hopt, "rh", NULL, airTypeInt, 0, 0, &(cam->rightHanded), NULL,
258 "use a right-handed UVN frame (V points down)");
259 hestOptAdd(&hopt, "dn", "near clip", airTypeDouble, 1, 1, &(cam->neer), "-2",
260 "position of near clipping plane, relative to look-at point");
261 hestOptAdd(&hopt, "df", "far clip", airTypeDouble, 1, 1, &(cam->faar), "2",
262 "position of far clipping plane, relative to look-at point");
263 hestOptAdd(&hopt, "or", NULL, airTypeInt, 0, 0, &(cam->orthographic), NULL,
264 "use orthogonal projection");
265 hestOptAdd(&hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange,
266 "-1 1", "range in U direction of image plane");
267 hestOptAdd(&hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange,
268 "-1 1", "range in V direction of image plane");
269 hestOptAdd(&hopt, "fv", "fov", airTypeDouble, 1, 1, &(cam->fov), "nan",
270 "if not NaN, vertical field-of-view, in degrees");
271
272 /* postscript-specific options */
273 hestOptAdd(&hopt, "gr", "glyph res", airTypeInt, 1, 1, &(gparm->facetRes),
274 "10", "(* postscript only *) "
275 "resolution of polygonalization of glyphs (all glyphs "
276 "other than the default box)");
277 hestOptAdd(&hopt, "wd", "3 widths", airTypeFloat, 3, 3, gparm->edgeWidth,
278 "0.8 0.4 0.0", "(* postscript only *) "
279 "width of edges drawn for three kinds of glyph "
280 "edges: silohuette, crease, non-crease");
281 hestOptAdd(&hopt, "psc", "scale", airTypeFloat, 1, 1, &(win->scale), "300",
282 "(* postscript only *) "
283 "scaling from screen space units to postscript units "
284 "(in points)");
285 hestOptAdd(&hopt, "ca", "angle", airTypeFloat, 1, 1, &creaseAngle, "70",
286 "(* postscript only *) "
287 "minimum crease angle");
288 hestOptAdd(&hopt, "nobg", NULL, airTypeInt, 0, 0, &nobg, NULL,
289 "(* postscript only *) "
290 "don't initially fill with background color");
291 hestOptAdd(&hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL,
292 "use slightly buggy rendering method suitable for "
293 "concave or self-occluding objects");
294
295 /* ray-traced-specific options */
296 hestOptAdd(&hopt, "is", "nx ny", airTypeInt, 2, 2, ires, "256 256",
297 "(* ray-traced only *) "
298 "image size (resolution) to render");
299 hestOptAdd(&hopt, "ns", "# samp", airTypeInt, 1, 1, &(eparm->numSamples),"4",
300 "(* ray-traced only *) "
301 "number of samples per pixel (must be a square number)");
302 if (airThreadCapable) {
303 hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1,
304 &(eparm->numThreads), "1",
305 "(* ray-traced only *) "
306 "number of threads to be used for rendering");
307 }
308 hestOptAdd(&hopt, "al", "B U V N E", airTypeFloat, 5, 5, buvne,
309 "0 -1 -1 -4 0.7",
310 "(* ray-traced only *) "
311 "brightness (B), view-space location (U V N), "
312 "and length of edge (E) "
313 "of a square area light source, for getting soft shadows. "
314 "Requires lots more samples \"-ns\" to converge. Use "
315 "brightness 0 (the default) to turn this off, and use "
316 "environment map-based shading (\"-emap\") instead. ");
317 hestOptAdd(&hopt, "ao", NULL, airTypeInt, 0, 0, &ambocc, NULL,
318 "set up 6 area lights in a box to approximate "
319 "ambient occlusion");
320 hestOptAdd(&hopt, "shadow", "s", airTypeFloat, 1, 1, &shadow, "1.0",
321 "the extent to which shadowing occurs");
322 hestOptAdd(&hopt, "hack", "hack", airTypeString, 1, 1, &hackFN, "",
323 "don't mind me");
324
325 /* input/output */
326 hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
327 "input diffusion tensor volume", NULL, NULL, nrrdHestNrrd);
328 hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
329 "output file");
330
331 airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
332 USAGE(_tend_glyphInfoL);
333 PARSE();
334 airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
335
336 /* set up slicing stuff */
337 if (!( -1 == slice[0] && -1 == slice[1] )) {
338 gparm->doSlice = AIR_TRUE;
339 gparm->sliceAxis = slice[0];
340 gparm->slicePos = slice[1];
341 gparm->sliceAnisoType = gparm->anisoType;
342 /* gparm->sliceOffset set by hest */
343 }
344
345 if (npos) {
346 fprintf(stderr, "!%s: have npos --> turning off onlyPositive \n", me);
347 gparm->onlyPositive = AIR_FALSE;
348 }
349
350 if (gparm->verbose) {
351 fprintf(stderr, "%s: verbose = %d\n", me, gparm->verbose);
352 }
353 if (tenGlyphGen(doRT ? NULL : glyph,
354 doRT ? scene : NULL,
355 gparm,
356 nin, npos, nslc)) {
357 airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
358 fprintf(stderr, "%s: trouble generating glyphs:\n%s\n", me, err);
359 airMopError(mop); return 1;
360 }
361 if (AIR_EXISTS(cam->fov)) {
362 if (limnCameraAspectSet(cam, ires[0], ires[1], nrrdCenterCell)) {
363 airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
364 fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err);
365 airMopError(mop); return 1;
366 }
367 }
368 cam->dist = 0;
369 cam->atRelative = AIR_TRUE;
370 if (limnCameraUpdate(cam)) {
371 airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
372 fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err);
373 airMopError(mop); return 1;
374 }
375 if (doRT) {
376 nraw = nrrdNew();
377 airMopAdd(mop, nraw, (airMopper)nrrdNuke, airMopAlways);
378 gstate = echoGlobalStateNew();
379 airMopAdd(mop, gstate, (airMopper)echoGlobalStateNix, airMopAlways);
380 eparm->shadow = shadow;
381 if (buvne[0] > 0) {
382 ELL_34M_EXTRACT(v2w, cam->V2W);
383 ELL_3MV_MUL(ldir, v2w, buvne+1);
384 ell_3v_perp_d(edir, ldir);
385 ELL_3V_NORM(edir, edir, len);
386 ELL_3V_CROSS(fdir, ldir, edir);
387 ELL_3V_NORM(fdir, fdir, len);
388 ELL_3V_SCALE(edir, buvne[4]/2, edir);
389 ELL_3V_SCALE(fdir, buvne[4]/2, fdir);
390 ELL_3V_ADD4(corn, cam->at, ldir, edir, fdir);
391 rect = echoObjectNew(scene, echoTypeRectangle);
392 echoRectangleSet(rect,
393 corn[0], corn[1], corn[2],
394 -edir[0]*2, -edir[1]*2, -edir[2]*2,
395 -fdir[0]*2, -fdir[1]*2, -fdir[2]*2);
396 echoColorSet(rect, 1, 1, 1, 1);
397 echoMatterLightSet(scene, rect, buvne[0], 0);
398 echoObjectAdd(scene, rect);
399 }
400 if (ambocc) {
401 double eye[3], llen;
402 ELL_3V_SUB(eye, cam->from, cam->at);
403 llen = 4*ELL_3V_LEN(eye);
404
405 ELL_3V_COPY(corn, cam->at);
406 corn[0] -= llen/2;
407 corn[1] -= llen/2;
408 corn[2] -= llen/2;
409 rect = echoObjectNew(scene, echoTypeRectangle);
410 echoRectangleSet(rect, corn[0], corn[1], corn[2],
411 llen, 0, 0, 0, llen, 0);
412 echoColorSet(rect, 1, 1, 1, 1);
413 echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
414 echoObjectAdd(scene, rect);
415 rect = echoObjectNew(scene, echoTypeRectangle);
416 echoRectangleSet(rect, corn[0], corn[1], corn[2],
417 0, 0, llen, llen, 0, 0);
418 echoColorSet(rect, 1, 1, 1, 1);
419 echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
420 echoObjectAdd(scene, rect);
421 rect = echoObjectNew(scene, echoTypeRectangle);
422 echoRectangleSet(rect, corn[0], corn[1], corn[2],
423 0, llen, 0, 0, 0, llen);
424 echoColorSet(rect, 1, 1, 1, 1);
425 echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
426 echoObjectAdd(scene, rect);
427
428 corn[0] += llen/2;
429 corn[1] += llen/2;
430 corn[2] += llen/2;
431 rect = echoObjectNew(scene, echoTypeRectangle);
432 echoRectangleSet(rect, corn[0], corn[1], corn[2],
433 0, -llen, 0, -llen, 0, 0);
434 echoColorSet(rect, 1, 1, 1, 1);
435 echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
436 echoObjectAdd(scene, rect);
437 rect = echoObjectNew(scene, echoTypeRectangle);
438 echoRectangleSet(rect, corn[0], corn[1], corn[2],
439 -llen, 0, 0, 0, 0, -llen);
440 echoColorSet(rect, 1, 1, 1, 1);
441 echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
442 echoObjectAdd(scene, rect);
443 rect = echoObjectNew(scene, echoTypeRectangle);
444 echoRectangleSet(rect, corn[0], corn[1], corn[2],
445 0, 0, -llen, 0, -llen, 0);
446 echoColorSet(rect, 1, 1, 1, 1);
447 echoMatterLightSet(scene, rect, 1, AIR_CAST(echoCol_t, llen));
448 echoObjectAdd(scene, rect);
449
450 }
451 eparm->imgResU = ires[0];
452 eparm->imgResV = ires[1];
453 eparm->jitterType = (eparm->numSamples > 1
454 ? echoJitterJitter
455 : echoJitterNone);
456 eparm->aperture = 0;
457 eparm->renderBoxes = AIR_FALSE;
458 eparm->seedRand = AIR_FALSE;
459 eparm->renderLights = AIR_FALSE;
460 ELL_3V_COPY(scene->bkgr, bg);
461 scene->envmap = emap;
462 if (!airStrlen(hackFN)) {
463 /* normal operation: one ray-tracing for one invocation */
464 if (echoRTRender(nraw, cam, scene, eparm, gstate)) {
465 airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways);
466 fprintf(stderr, "%s: trouble ray-tracing %s\n", me, err);
467 airMopError(mop);
468 return 1;
469 }
470 if (nrrdSave(outS, nraw, NULL)) {
471 airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
472 fprintf(stderr, "%s: trouble saving ray-tracing output %s\n", me, err);
473 airMopError(mop);
474 return 1;
475 }
476 } else {
477 /* hack: multiple renderings per invocation */
478 if (!(hackF = airFopen(hackFN, stdin, "rb"))) {
479 fprintf(stderr, "%s: couldn't fopen(\"%s\",\"rb\"): %s\n",
480 me, hackFN, strerror(errno));
481 airMopError(mop); return 1;
482 }
483 if (_tendGlyphReadCams(ires, &hackcams, &hacknumcam, hackF)) {
484 airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
485 fprintf(stderr, "%s: trouble reading frames %s\n", me, err);
486 airMopError(mop);
487 return 1;
488 }
489 eparm->imgResU = ires[0];
490 eparm->imgResV = ires[1];
491 hackmax[1] = ires[0]-1;
492 hackmax[2] = ires[1]-1;
493 hacknrgb = nrrdNew();
494 hacknpng = nrrdNew();
495 airMopAdd(mop, hacknrgb, (airMopper)nrrdNuke, airMopAlways);
496 airMopAdd(mop, hacknpng, (airMopper)nrrdNuke, airMopAlways);
497 hackrange = nrrdRangeNew(0.0, 1.0);
498 airMopAdd(mop, hackrange, (airMopper)nrrdRangeNix, airMopAlways);
499 for (hackci=0; hackci<hacknumcam; hackci++) {
500 memcpy(cam, hackcams + hackci, sizeof(limnCamera));
501 /* rightHanded and orthographic not handled nicely */
502
503 if (rect) {
504 if (limnCameraUpdate(cam)) {
505 airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
506 fprintf(stderr, "%s: trouble with camera:\n%s\n", me, err);
507 airMopError(mop); return 1;
508 }
509 ELL_34M_EXTRACT(v2w, cam->V2W);
510 ELL_3MV_MUL(ldir, v2w, buvne+1);
511 ell_3v_perp_d(edir, ldir);
512 ELL_3V_NORM(edir, edir, len);
513 ELL_3V_CROSS(fdir, ldir, edir);
514 ELL_3V_NORM(fdir, fdir, len);
515 ELL_3V_SCALE(edir, buvne[4]/2, edir);
516 ELL_3V_SCALE(fdir, buvne[4]/2, fdir);
517 ELL_3V_ADD4(corn, cam->at, ldir, edir, fdir);
518 echoRectangleSet(rect,
519 corn[0], corn[1], corn[2],
520 edir[0]*2, edir[1]*2, edir[2]*2,
521 fdir[0]*2, fdir[1]*2, fdir[2]*2);
522 }
523
524 if (echoRTRender(nraw, cam, scene, eparm, gstate)) {
525 airMopAdd(mop, err = biffGetDone(ECHO), airFree, airMopAlways);
526 fprintf(stderr, "%s: trouble ray-tracing %s\n", me, err);
527 airMopError(mop);
528 return 1;
529 }
530 sprintf(hackoutFN, "%04d.png", hackci);
531 if (nrrdCrop(hacknrgb, nraw, hackmin, hackmax)
532 || nrrdQuantize(hacknpng, hacknrgb, hackrange, 8)
533 || nrrdSave(hackoutFN, hacknpng, NULL)) {
534 airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
535 fprintf(stderr, "%s: trouble saving output %s\n", me, err);
536 airMopError(mop);
537 return 1;
538 }
539 }
540 }
541 } else {
542 if (!(win->file = airFopen(outS, stdout, "wb"))) {
543 fprintf(stderr, "%s: couldn't fopen(\"%s\",\"wb\"): %s\n",
544 me, outS, strerror(errno));
545 airMopError(mop); return 1;
546 }
547 airMopAdd(mop, win->file, (airMopper)airFclose, airMopAlways);
548 cam->neer = -0.000000001;
549 cam->faar = 0.0000000001;
550 win->ps.lineWidth[limnEdgeTypeBackFacet] = 0;
551 win->ps.lineWidth[limnEdgeTypeBackCrease] = 0;
552 win->ps.lineWidth[limnEdgeTypeContour] = gparm->edgeWidth[0];
553 win->ps.lineWidth[limnEdgeTypeFrontCrease] = gparm->edgeWidth[1];
554 win->ps.lineWidth[limnEdgeTypeFrontFacet] = gparm->edgeWidth[2];
555 win->ps.lineWidth[limnEdgeTypeBorder] = 0;
556 /* win->ps.lineWidth[limnEdgeTypeFrontCrease]; */
557 win->ps.creaseAngle = creaseAngle;
558 win->ps.noBackground = nobg;
559 ELL_3V_COPY(win->ps.bg, bg);
560 ELL_3V_COPY(win->ps.edgeColor, edgeColor);
561 if (limnObjectRender(glyph, cam, win)
562 || (concave
563 ? limnObjectPSDrawConcave(glyph, cam, emap, win)
564 : limnObjectPSDraw(glyph, cam, emap, win))) {
565 airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways);
566 fprintf(stderr, "%s: trouble drawing glyphs:\n%s\n", me, err);
567 airMopError(mop); return 1;
568 }
569 }
570
571 airMopOkay(mop);
572 return 0;
573 }
574 /* TEND_CMD(glyph, INFO); */
575 unrrduCmd tend_glyphCmd = { "glyph", INFO, tend_glyphMain };
576