1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2013, 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 <teem/air.h> |
25 |
|
|
#include <teem/biff.h> |
26 |
|
|
#include <teem/ell.h> |
27 |
|
|
#include <teem/nrrd.h> |
28 |
|
|
#include <teem/limn.h> |
29 |
|
|
#include <teem/hoover.h> |
30 |
|
|
#include <teem/mite.h> |
31 |
|
|
|
32 |
|
|
static const char *miteInfo = |
33 |
|
|
("A simple but effective little volume renderer."); |
34 |
|
|
|
35 |
|
|
int |
36 |
|
|
main(int argc, const char *argv[]) { |
37 |
|
|
airArray *mop; |
38 |
|
|
hestOpt *hopt=NULL; |
39 |
|
|
hestParm *hparm=NULL; |
40 |
|
|
miteUser *muu; |
41 |
|
|
const char *me; |
42 |
|
|
char *errS, *outS, *shadeStr, *normalStr, debugStr[AIR_STRLEN_MED]; |
43 |
|
|
int renorm, baseDim, verbPix[2], offfr; |
44 |
|
|
int E, Ecode, Ethread; |
45 |
|
|
float ads[3], isScale; |
46 |
|
|
double turn, eye[3], eyedist, gmc; |
47 |
|
|
double v[NRRD_SPACE_DIM_MAX]; |
48 |
|
|
Nrrd *nin; |
49 |
|
|
|
50 |
|
|
me = argv[0]; |
51 |
|
|
mop = airMopNew(); |
52 |
|
|
hparm = hestParmNew(); |
53 |
|
|
airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); |
54 |
|
|
muu = miteUserNew(); |
55 |
|
|
airMopAdd(mop, muu, (airMopper)miteUserNix, airMopAlways); |
56 |
|
|
|
57 |
|
|
hparm->respFileEnable = AIR_TRUE; |
58 |
|
|
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; |
59 |
|
|
hestOptAdd(&hopt, "i", "nsin", airTypeOther, 1, 1, &(muu->nsin), "", |
60 |
|
|
"input scalar volume to render", NULL, NULL, nrrdHestNrrd); |
61 |
|
|
hestOptAdd(&hopt, "vi", "nvin", airTypeOther, 1, 1, &(muu->nvin), "", |
62 |
|
|
"input vector volume to render", NULL, NULL, nrrdHestNrrd); |
63 |
|
|
hestOptAdd(&hopt, "ti", "ntin", airTypeOther, 1, 1, &(muu->ntin), "", |
64 |
|
|
"input tensor volume to render", NULL, NULL, nrrdHestNrrd); |
65 |
|
|
hestOptAdd(&hopt, "txf", "nin", airTypeOther, 1, -1, &(muu->ntxf), NULL, |
66 |
|
|
"one or more transfer functions", |
67 |
|
|
&(muu->ntxfNum), NULL, nrrdHestNrrd); |
68 |
|
|
limnHestCameraOptAdd(&hopt, muu->hctx->cam, |
69 |
|
|
NULL, "0 0 0", "0 0 1", |
70 |
|
|
NULL, NULL, NULL, |
71 |
|
|
"nan nan", "nan nan", "20"); |
72 |
|
|
hestOptAdd(&hopt, "offfr", NULL, airTypeInt, 0, 0, &offfr, NULL, |
73 |
|
|
"the given eye point (\"-fr\") is to be interpreted " |
74 |
|
|
"as an offset from the at point."); |
75 |
|
|
hestOptAdd(&hopt, "ffr", "fake from", airTypeDouble, 3, 3, |
76 |
|
|
&(muu->fakeFrom), "nan nan nan", |
77 |
|
|
"eye point to use for view-dependent transfer functions. " |
78 |
|
|
"By default (not using this option), the point used is the " |
79 |
|
|
"normally specified camera eye point."); |
80 |
|
|
hestOptAdd(&hopt, "turn", "angle", airTypeDouble, 1, 1, &turn, "0.0", |
81 |
|
|
"angle (degrees) by which to rotate the from point around " |
82 |
|
|
"true up, for making stereo pairs. Positive means move " |
83 |
|
|
"towards positive U (the right)"); |
84 |
|
|
hestOptAdd(&hopt, "am", "ambient", airTypeFloat, 3, 3, muu->lit->amb, |
85 |
|
|
"1 1 1", "ambient light color"); |
86 |
|
|
hestOptAdd(&hopt, "ld", "light pos", airTypeFloat, 3, 3, muu->lit->_dir[0], |
87 |
|
|
"0 0 -1", "view space light position (extended to infinity)"); |
88 |
|
|
hestOptAdd(&hopt, "is", "image size", airTypeInt, 2, 2, muu->hctx->imgSize, |
89 |
|
|
"256 256", "image dimensions"); |
90 |
|
|
hestOptAdd(&hopt, "iss", "scale", airTypeFloat, 1, 1, &isScale, "1.0", |
91 |
|
|
"scaling of image size (from \"is\")"); |
92 |
|
|
hestOptAdd(&hopt, "ads", "ka kd ks", airTypeFloat, 3, 3, ads, |
93 |
|
|
"0.1 0.6 0.3", "phong components"); |
94 |
|
|
hestOptAdd(&hopt, "sp", "spec pow", mite_at, 1, 1, |
95 |
|
|
&(muu->rangeInit[miteRangeSP]), "30", "phong specular power"); |
96 |
|
|
hestOptAdd(&hopt, "k00", "kernel", airTypeOther, 1, 1, |
97 |
|
|
&(muu->ksp[gageKernel00]), |
98 |
|
|
"tent", "value reconstruction kernel", |
99 |
|
|
NULL, NULL, nrrdHestKernelSpec); |
100 |
|
|
hestOptAdd(&hopt, "k11", "kernel", airTypeOther, 1, 1, |
101 |
|
|
&(muu->ksp[gageKernel11]), |
102 |
|
|
"cubicd:1,0", "first derivative kernel", |
103 |
|
|
NULL, NULL, nrrdHestKernelSpec); |
104 |
|
|
hestOptAdd(&hopt, "k22", "kernel", airTypeOther, 1, 1, |
105 |
|
|
&(muu->ksp[gageKernel22]), |
106 |
|
|
"cubicdd:1,0", "second derivative kernel", |
107 |
|
|
NULL, NULL, nrrdHestKernelSpec); |
108 |
|
|
hestOptAdd(&hopt, "ss", "shading spec", airTypeString, 1, 1, &shadeStr, |
109 |
|
|
"phong:gage(scalar:n)", "how to do shading"); |
110 |
|
|
hestOptAdd(&hopt, "ns", "normal spec", airTypeString, 1, 1, &normalStr, |
111 |
|
|
"", "\"normal\" to use for those miteVal's that need one"); |
112 |
|
|
hestOptAdd(&hopt, "side", "normal side", airTypeInt, 1, 1, |
113 |
|
|
&(muu->normalSide), |
114 |
|
|
"1", "how to interpret gradients as normals:\n " |
115 |
|
|
"\b\bo \"1\": normal points to lower values (higher == " |
116 |
|
|
"more \"inside\")\n " |
117 |
|
|
"\b\bo \"0\": \"two-sided\": dot-products are abs()'d\n " |
118 |
|
|
"\b\bo \"-1\": normal points to higher values (lower == " |
119 |
|
|
"more \"inside\")"); |
120 |
|
|
hestOptAdd(&hopt, "rn", NULL, airTypeBool, 0, 0, &renorm, NULL, |
121 |
|
|
"renormalize kernel weights at each new sample location. " |
122 |
|
|
"\"Accurate\" kernels don't need this; doing it always " |
123 |
|
|
"makes things go slower"); |
124 |
|
|
hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0", |
125 |
|
|
"For curvature-based transfer functions, set curvature to " |
126 |
|
|
"zero when gradient magnitude is below this"); |
127 |
|
|
hestOptAdd(&hopt, "step", "size", airTypeDouble, 1, 1, &(muu->rayStep), |
128 |
|
|
"0.01", "step size along ray in world space"); |
129 |
|
|
hestOptAdd(&hopt, "ref", "size", airTypeDouble, 1, 1, &(muu->refStep), |
130 |
|
|
"0.01", "\"reference\" step size (world space) for doing " |
131 |
|
|
"opacity correction in compositing"); |
132 |
|
|
hestOptAdd(&hopt, "vp", "verbose pixel", airTypeInt, 2, 2, verbPix, |
133 |
|
|
"-1 -1", "pixel for which to turn on verbose messages"); |
134 |
|
|
hestOptAdd(&hopt, "n1", "near1", airTypeDouble, 1, 1, &(muu->opacNear1), |
135 |
|
|
"0.99", "opacity close enough to 1.0 to terminate ray"); |
136 |
|
|
hestOptAdd(&hopt, "nt", "# threads", airTypeInt, 1, 1, |
137 |
|
|
&(muu->hctx->numThreads), "1", |
138 |
|
|
(airThreadCapable |
139 |
|
|
? "number of threads hoover should use" |
140 |
|
|
: "if pthreads where enabled in this Teem build, this is how " |
141 |
|
|
"you would control the number of threads hoover should use")); |
142 |
|
|
hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, |
143 |
|
|
NULL, "file to write output nrrd to"); |
144 |
|
|
hestParseOrDie(hopt, argc-1, argv+1, hparm, |
145 |
|
|
me, miteInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); |
146 |
|
|
airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); |
147 |
|
|
airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); |
148 |
|
|
|
149 |
|
|
if (muu->nsin) { |
150 |
|
|
nin = muu->nsin; |
151 |
|
|
baseDim = 0; |
152 |
|
|
} else if (muu->nvin) { |
153 |
|
|
nin = muu->nvin; |
154 |
|
|
baseDim = 1; |
155 |
|
|
} else if (muu->ntin) { |
156 |
|
|
nin = muu->ntin; |
157 |
|
|
baseDim = 1; |
158 |
|
|
} else { |
159 |
|
|
fprintf(stderr, "%s: didn't get any volumes to render!\n", me); |
160 |
|
|
airMopError(mop); |
161 |
|
|
return 1; |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
/* finish processing command-line args */ |
165 |
|
|
muu->rangeInit[miteRangeKa] = ads[0]; |
166 |
|
|
muu->rangeInit[miteRangeKd] = ads[1]; |
167 |
|
|
muu->rangeInit[miteRangeKs] = ads[2]; |
168 |
|
|
gageParmSet(muu->gctx0, gageParmGradMagCurvMin, gmc); |
169 |
|
|
gageParmSet(muu->gctx0, gageParmRenormalize, |
170 |
|
|
renorm ? AIR_TRUE : AIR_FALSE); |
171 |
|
|
muu->verbUi = verbPix[0]; |
172 |
|
|
muu->verbVi = verbPix[1]; |
173 |
|
|
if (offfr) { |
174 |
|
|
ELL_3V_INCR(muu->hctx->cam->from, muu->hctx->cam->at); |
175 |
|
|
} |
176 |
|
|
muu->hctx->imgSize[0] = AIR_CAST(int, isScale*muu->hctx->imgSize[0]); |
177 |
|
|
muu->hctx->imgSize[1] = AIR_CAST(int, isScale*muu->hctx->imgSize[1]); |
178 |
|
|
|
179 |
|
|
muu->nout = nrrdNew(); |
180 |
|
|
airMopAdd(mop, muu->nout, (airMopper)nrrdNuke, airMopAlways); |
181 |
|
|
ELL_3V_SET(muu->lit->col[0], 1, 1, 1); |
182 |
|
|
muu->lit->on[0] = AIR_TRUE; |
183 |
|
|
muu->lit->vsp[0] = AIR_TRUE; |
184 |
|
|
if (AIR_EXISTS(muu->hctx->cam->uRange[0]) |
185 |
|
|
&& AIR_EXISTS(muu->hctx->cam->uRange[1]) |
186 |
|
|
&& AIR_EXISTS(muu->hctx->cam->vRange[0]) |
187 |
|
|
&& AIR_EXISTS(muu->hctx->cam->vRange[1])) { |
188 |
|
|
/* someone went to the trouble of setting the U,V minmax, which |
189 |
|
|
means they probably don't want the "fov"-based view window, |
190 |
|
|
whether or not the "fov" value came from the command-line or |
191 |
|
|
from the (unavoidable) default */ |
192 |
|
|
muu->hctx->cam->fov = AIR_NAN; |
193 |
|
|
} |
194 |
|
|
if (limnCameraAspectSet(muu->hctx->cam, |
195 |
|
|
muu->hctx->imgSize[0], muu->hctx->imgSize[1], |
196 |
|
|
nrrdCenterCell) |
197 |
|
|
|| limnCameraUpdate(muu->hctx->cam) |
198 |
|
|
|| limnLightUpdate(muu->lit, muu->hctx->cam)) { |
199 |
|
|
airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); |
200 |
|
|
fprintf(stderr, "%s: trouble setting camera:\n%s\n", me, errS); |
201 |
|
|
airMopError(mop); |
202 |
|
|
return 1; |
203 |
|
|
} |
204 |
|
|
if (turn) { |
205 |
|
|
turn *= AIR_PI/180; |
206 |
|
|
ELL_3V_SUB(eye, muu->hctx->cam->from, muu->hctx->cam->at); |
207 |
|
|
ELL_3V_NORM(eye, eye, eyedist); |
208 |
|
|
ELL_3V_SCALE_ADD2(muu->hctx->cam->from, |
209 |
|
|
cos(turn), eye, |
210 |
|
|
sin(turn), muu->hctx->cam->U); |
211 |
|
|
ELL_3V_SCALE(muu->hctx->cam->from, eyedist, muu->hctx->cam->from); |
212 |
|
|
if (limnCameraUpdate(muu->hctx->cam)) { |
213 |
|
|
airMopAdd(mop, errS = biffGetDone(LIMN), airFree, airMopAlways); |
214 |
|
|
fprintf(stderr, "%s: trouble setting camera (again):\n%s\n", me, errS); |
215 |
|
|
airMopError(mop); |
216 |
|
|
return 1; |
217 |
|
|
} |
218 |
|
|
} |
219 |
|
|
/* |
220 |
|
|
fprintf(stderr, "%s: camera info\n", me); |
221 |
|
|
fprintf(stderr, " U = {%g,%g,%g}\n", |
222 |
|
|
muu->hctx->cam->U[0], muu->hctx->cam->U[1], muu->hctx->cam->U[2]); |
223 |
|
|
fprintf(stderr, " V = {%g,%g,%g}\n", |
224 |
|
|
muu->hctx->cam->V[0], muu->hctx->cam->V[1], muu->hctx->cam->V[2]); |
225 |
|
|
fprintf(stderr, " N = {%g,%g,%g}\n", |
226 |
|
|
muu->hctx->cam->N[0], muu->hctx->cam->N[1], muu->hctx->cam->N[2]); |
227 |
|
|
*/ |
228 |
|
|
airStrcpy(muu->shadeStr, AIR_STRLEN_MED, shadeStr); |
229 |
|
|
airStrcpy(muu->normalStr, AIR_STRLEN_MED, normalStr); |
230 |
|
|
if (0) { |
231 |
|
|
muu->hctx->volSize[0] = nin->axis[baseDim+0].size; |
232 |
|
|
muu->hctx->volSize[1] = nin->axis[baseDim+1].size; |
233 |
|
|
muu->hctx->volSize[2] = nin->axis[baseDim+2].size; |
234 |
|
|
/* Get the proper spacing from the NRRD volume */ |
235 |
|
|
nrrdSpacingCalculate ( nin, baseDim+0, &(muu->hctx->volSpacing[0]), v ); |
236 |
|
|
nrrdSpacingCalculate ( nin, baseDim+1, &(muu->hctx->volSpacing[1]), v ); |
237 |
|
|
nrrdSpacingCalculate ( nin, baseDim+2, &(muu->hctx->volSpacing[2]), v ); |
238 |
|
|
} else { |
239 |
|
|
if (gageShapeSet(muu->shape, nin, baseDim)) { |
240 |
|
|
fprintf(stderr, "%s: problem with shape:\n%s\n", |
241 |
|
|
me, errS = biffGetDone(GAGE)); free(errS); |
242 |
|
|
airMopError(mop); |
243 |
|
|
return 1; |
244 |
|
|
} |
245 |
|
|
muu->hctx->shape = muu->shape; |
246 |
|
|
} |
247 |
|
|
muu->hctx->user = muu; |
248 |
|
|
muu->hctx->renderBegin = (hooverRenderBegin_t *)miteRenderBegin; |
249 |
|
|
muu->hctx->threadBegin = (hooverThreadBegin_t *)miteThreadBegin; |
250 |
|
|
muu->hctx->rayBegin = (hooverRayBegin_t *)miteRayBegin; |
251 |
|
|
muu->hctx->sample = (hooverSample_t *)miteSample; |
252 |
|
|
muu->hctx->rayEnd = (hooverRayEnd_t *)miteRayEnd; |
253 |
|
|
muu->hctx->threadEnd = (hooverThreadEnd_t *)miteThreadEnd; |
254 |
|
|
muu->hctx->renderEnd = (hooverRenderEnd_t *)miteRenderEnd; |
255 |
|
|
|
256 |
|
|
if (!airThreadCapable && 1 != muu->hctx->numThreads) { |
257 |
|
|
fprintf(stderr, "%s: This Teem not compiled with " |
258 |
|
|
"multi-threading support.\n", me); |
259 |
|
|
fprintf(stderr, "%s: ==> can't use %d threads; only using 1\n", |
260 |
|
|
me, muu->hctx->numThreads); |
261 |
|
|
muu->hctx->numThreads = 1; |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
fprintf(stderr, "%s: rendering ... ", me); fflush(stderr); |
265 |
|
|
|
266 |
|
|
E = hooverRender(muu->hctx, &Ecode, &Ethread); |
267 |
|
|
if (E) { |
268 |
|
|
if (hooverErrInit == E) { |
269 |
|
|
errS = biffGetDone(HOOVER); |
270 |
|
|
} else { |
271 |
|
|
errS = biffGetDone(MITE); |
272 |
|
|
} |
273 |
|
|
airMopAdd(mop, errS, airFree, airMopAlways); |
274 |
|
|
fprintf(stderr, "%s: %s error (code %d, thread %d):\n%s\n", |
275 |
|
|
me, airEnumStr(hooverErr, E), Ecode, Ethread, errS); |
276 |
|
|
airMopError(mop); |
277 |
|
|
return 1; |
278 |
|
|
} |
279 |
|
|
fprintf(stderr, "\n"); |
280 |
|
|
fprintf(stderr, "%s: rendering time = %g secs\n", me, muu->rendTime); |
281 |
|
|
fprintf(stderr, "%s: sampling rate = %g Khz\n", me, muu->sampRate); |
282 |
|
|
if (muu->ndebug) { |
283 |
|
|
/* if its been generated, we should save it */ |
284 |
|
|
sprintf(debugStr, "%04d-%04d-debug.nrrd", verbPix[0], verbPix[1]); |
285 |
|
|
if (nrrdSave(debugStr, muu->ndebug, NULL)) { |
286 |
|
|
airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); |
287 |
|
|
fprintf(stderr, "%s: trouble saving ray debug:\n%s\n", me, errS); |
288 |
|
|
airMopError(mop); |
289 |
|
|
return 1; |
290 |
|
|
} |
291 |
|
|
} |
292 |
|
|
if (nrrdSave(outS, muu->nout, NULL)) { |
293 |
|
|
airMopAdd(mop, errS = biffGetDone(NRRD), airFree, airMopAlways); |
294 |
|
|
fprintf(stderr, "%s: trouble saving image:\n%s\n", me, errS); |
295 |
|
|
airMopError(mop); |
296 |
|
|
return 1; |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
airMopOkay(mop); |
300 |
|
|
return 0; |
301 |
|
|
} |
302 |
|
|
|