Tachyon (current)  Current Main Branch
vol.c
Go to the documentation of this file.
1 /*
2  * vol.c - Volume rendering helper routines etc.
3  *
4  * (C) Copyright 1994-2022 John E. Stone
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * $Id: vol.c,v 1.56 2022/02/18 17:55:28 johns Exp $
8  *
9  */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <math.h>
15 
16 #define TACHYON_INTERNAL 1
17 #include "tachyon.h"
18 #include "macros.h"
19 #include "vector.h"
20 #include "util.h"
21 #include "parallel.h"
22 #include "threads.h"
23 #include "vol.h"
24 #include "box.h"
25 #include "trace.h"
26 #include "ui.h"
27 #include "shade.h"
28 #include "texture.h"
29 
30 int scalarvol_bbox(void * obj, vector * min, vector * max) {
31  box * b = (box *) obj;
32 
33  *min = b->min;
34  *max = b->max;
35 
36  return 1;
37 }
38 
39 #if 0 /* not yet... */
40 static object_methods scalarvol_methods = {
41  (void (*)(void *, void *))(box_intersect),
42  (void (*)(void *, void *, void *, void *))(box_normal),
44  free
45 };
46 #endif
47 
48 void * newscalarvol(void * voidtex, vector min, vector max,
49  int xs, int ys, int zs, const char * fname,
50  scalarvol * invol) {
51  standard_texture * tx, * tex;
52  scalarvol * vol;
53 
54  tex=(standard_texture *) voidtex;
55  tex->flags = RT_TEXTURE_NOFLAGS; /* doesn't cast a shadow */
56 
57  tx=malloc(sizeof(standard_texture));
58 
59  /* is the volume data already loaded? */
60  if (invol==NULL) {
61  vol=malloc(sizeof(scalarvol));
62  vol->loaded=0;
63  vol->data=NULL;
64  } else {
65  vol=invol;
66  }
67 
68  vol->opacity=tex->opacity;
69  vol->xres=xs;
70  vol->yres=ys;
71  vol->zres=zs;
72  strcpy(vol->name, fname);
73 
74  tx->ctr.x = 0.0;
75  tx->ctr.y = 0.0;
76  tx->ctr.z = 0.0;
77  tx->rot = tx->ctr;
78  tx->scale = tx->ctr;
79  tx->uaxs = tx->ctr;
80  tx->vaxs = tx->ctr;
81 
82  tx->flags = RT_TEXTURE_NOFLAGS;
83 
84  tx->col = tex->col;
85  tx->ambient = 1.0;
86  tx->diffuse = 0.0;
87  tx->phong = 0.0;
88  tx->phongexp = 0.0;
89  tx->phongtype = 0;
90  tx->specular = 0.0;
91  tx->opacity = 1.0;
92  tx->outline = 0.0;
93  tx->outlinewidth = 0.0;
94  tx->img = vol;
95  tx->texfunc = (color(*)(const void *, const void *, void *))(scalar_volume_texture);
96 
97  tx->obj = (void *) newbox(tx, min, max); /* XXX hack!! */
98 
99  /* Force load of volume data so that we don't have to do mutex locks */
100  /* inside the rendering threads */
101  if (!vol->loaded) {
102  LoadVol(vol);
103  }
104 
105  /* check if loading succeeded */
106  if (!vol->loaded) {
107  tx->texfunc = (color(*)(const void *, const void *, void *))(constant_texture);
108  tx->img = NULL;
109  free(vol);
110  }
111 
112  return (void *) tx->obj;
113 }
114 
115 
116 color VoxelColor(flt scalar) {
117  color col;
118 
119  if (scalar > 1.0)
120  scalar = 1.0;
121 
122  if (scalar < 0.0)
123  scalar = 0.0;
124 
125  if (scalar < 0.25) {
126  col.r = scalar * 4.0;
127  col.g = 0.0;
128  col.b = 0.0;
129  }
130  else {
131  if (scalar < 0.75) {
132  col.r = 1.0;
133  col.g = (scalar - 0.25) * 2.0;
134  col.b = 0.0;
135  }
136  else {
137  col.r = 1.0;
138  col.g = 1.0;
139  col.b = (scalar - 0.75) * 4.0;
140  }
141  }
142 
143  return col;
144 }
145 
146 
147 color scalar_volume_texture(const vector * hit, const texture * tx, ray * ry) {
148  color col, col2;
149  box * bx;
150  flt a, tx1, tx2, ty1, ty2, tz1, tz2;
151  flt tnear, tfar;
152  flt t, tdist, dt, sum, tt;
153  vector pnt, bln, bln_1;
154  scalarvol * vol;
155  flt scalar, transval;
156  long x, y, z, lxyres, lxres;
157  unsigned char * ptr;
158  standard_texture * tex = (standard_texture *) tx;
159  const flt voxel_inv = 1.0f / 255.0;
160  bx=(box *) tex->obj;
161  vol=(scalarvol *) ((standard_texture *) bx->tex)->img;
162 
163  col.r=0.0;
164  col.g=0.0;
165  col.b=0.0;
166 
167  tnear= -FHUGE;
168  tfar= FHUGE;
169 
170  if (ry->d.x == 0.0) {
171  if ((ry->o.x < bx->min.x) || (ry->o.x > bx->max.x)) return col;
172  } else {
173  tx1 = (bx->min.x - ry->o.x) / ry->d.x;
174  tx2 = (bx->max.x - ry->o.x) / ry->d.x;
175  if (tx1 > tx2) { a=tx1; tx1=tx2; tx2=a; }
176  if (tx1 > tnear) tnear=tx1;
177  if (tx2 < tfar) tfar=tx2;
178  }
179  if (tnear > tfar) return col;
180  if (tfar < 0.0) return col;
181 
182  if (ry->d.y == 0.0) {
183  if ((ry->o.y < bx->min.y) || (ry->o.y > bx->max.y)) return col;
184  } else {
185  ty1 = (bx->min.y - ry->o.y) / ry->d.y;
186  ty2 = (bx->max.y - ry->o.y) / ry->d.y;
187  if (ty1 > ty2) { a=ty1; ty1=ty2; ty2=a; }
188  if (ty1 > tnear) tnear=ty1;
189  if (ty2 < tfar) tfar=ty2;
190  }
191  if (tnear > tfar) return col;
192  if (tfar < 0.0) return col;
193 
194  if (ry->d.z == 0.0) {
195  if ((ry->o.z < bx->min.z) || (ry->o.z > bx->max.z)) return col;
196  } else {
197  tz1 = (bx->min.z - ry->o.z) / ry->d.z;
198  tz2 = (bx->max.z - ry->o.z) / ry->d.z;
199  if (tz1 > tz2) { a=tz1; tz1=tz2; tz2=a; }
200  if (tz1 > tnear) tnear=tz1;
201  if (tz2 < tfar) tfar=tz2;
202  }
203 
204 #if 0
205  /* XXX this is where we cause early exit if the volumetric */
206  /* object intersects other geometric objects */
207 
208  /* stop at closest intersection from other objects */
209  if (ry->maxdist < tfar)
210  tfar = ry->maxdist;
211 #endif
212 
213  if (tnear > tfar) return col;
214  if (tfar < 0.0) return col;
215 
216  if (tnear < 0.0) tnear=0.0;
217 
218  tdist=SQRT(vol->xres*vol->xres + vol->yres*vol->yres + vol->zres*vol->zres);
219  tt = (vol->opacity / tdist);
220 
221  bln.x=FABS(bx->min.x - bx->max.x);
222  bln.y=FABS(bx->min.y - bx->max.y);
223  bln.z=FABS(bx->min.z - bx->max.z);
224  dt=SQRT(bln.x*bln.x + bln.y*bln.y + bln.z*bln.z) / tdist;
225  sum=0.0;
226 
227  /* promote int types to long to prevent integer overflows in index calcs */
228  lxyres = vol->xres * vol->yres;
229  lxres = vol->xres;
230 
231 
232  /* avoid divides in the voxel traversal loop */
233  bln_1.x = 1.0 / bln.x;
234  bln_1.y = 1.0 / bln.y;
235  bln_1.z = 1.0 / bln.z;
236 
237  for (t=tnear; t<=tfar; t+=dt) {
238  pnt.x=((ry->o.x + (ry->d.x * t)) - bx->min.x) * bln_1.x;
239  pnt.y=((ry->o.y + (ry->d.y * t)) - bx->min.y) * bln_1.y;
240  pnt.z=((ry->o.z + (ry->d.z * t)) - bx->min.z) * bln_1.z;
241 
242  x=(long) ((vol->xres - 1.5) * pnt.x + 0.5);
243  y=(long) ((vol->yres - 1.5) * pnt.y + 0.5);
244  z=(long) ((vol->zres - 1.5) * pnt.z + 0.5);
245 
246  ptr = vol->data + ((lxyres * z) + (lxres * y) + x);
247 
248  scalar = ((int) ptr[0]) * voxel_inv;
249 
250  sum += tt * scalar;
251 
252  transval = tt * scalar;
253 
254  col2 = VoxelColor(scalar);
255 
256  if (sum < 1.0) {
257  col.r += transval * col2.r;
258  col.g += transval * col2.g;
259  col.b += transval * col2.b;
260  if (sum < 0.0) sum=0.0;
261  } else {
262  sum=1.0;
263  }
264  }
265 
266 
267  /* XXX this has to be changed in order to allow volumetric objects */
268  /* to intersect with geometric objects */
269 
270  if (sum < 1.0) { /* spawn transmission rays / refraction */
271  color transcol;
272  shadedata shadevars;
273  shadevars.hit=*hit;
274  transcol = shade_transmission(ry, &shadevars, 1.0 - sum);
275 
276  col.r += transcol.r; /* add the transmitted ray */
277  col.g += transcol.g; /* to the diffuse and */
278  col.b += transcol.b; /* transmission total.. */
279  }
280 
281  return col;
282 }
283 
284 void LoadVol(scalarvol * vol) {
285  FILE * dfile;
286 
287  dfile=fopen(vol->name, "r");
288  if (dfile==NULL) {
289  char msgtxt[2048];
290  sprintf(msgtxt, "Can't load volume %s, using object color", vol->name);
291  rt_ui_message(MSG_ERR, msgtxt);
292  return;
293  }
294 
295  if (rt_mynode()==0) {
296  char msgtxt[2048];
297  sprintf(msgtxt, "Loading %dx%dx%d volume set from %s",
298  vol->xres, vol->yres, vol->zres, vol->name);
299  rt_ui_message(MSG_0, msgtxt);
300  }
301  vol->data = malloc(vol->xres * vol->yres * vol->zres);
302 
303  if (fread(vol->data, (vol->xres * vol->yres * vol->zres), 1, dfile) == 1) {
304  vol->loaded=1;
305  } else {
306  char msgtxt[2048];
307  sprintf(msgtxt, "Can't load volume %s, using object color", vol->name);
308  rt_ui_message(MSG_ERR, msgtxt);
309  }
310 
311  fclose(dfile);
312 }
313 
314 
color shade_transmission(ray *incident, const shadedata *shadevars, flt trans)
Definition: shade.c:533
RT_OBJECT_HEAD vector min
minimum vertex coordinate
Definition: box.h:16
box * newbox(void *tex, vector min, vector max)
Definition: box.c:40
int loaded
Definition: animskull.c:20
void rt_ui_message(int level, char *msg)
Definition: ui.c:31
void box_intersect(const box *bx, ray *ry)
Definition: box.c:53
color scalar_volume_texture(const vector *hit, const texture *tx, ray *ry)
Definition: vol.c:147
vector max
maximum vertex coordinate
Definition: box.h:17
Tachyon cross-platform thread creation and management, atomic operations, and CPU feature query APIs...
double flt
generic floating point number, using double
Definition: tachyon.h:47
int rt_mynode(void)
distributed memory parallel node rank
Definition: api.c:49
color VoxelColor(flt scalar)
Definition: vol.c:116
#define MSG_ERR
Definition: ui.h:18
Tachyon cross-platform timers, special math function wrappers, and RNGs.
int xres
Definition: animskull.c:21
int zres
Definition: animskull.c:23
void LoadVol(scalarvol *vol)
Definition: vol.c:284
#define SQRT(x)
Definition: util.h:31
void * newscalarvol(void *voidtex, vector min, vector max, int xs, int ys, int zs, const char *fname, scalarvol *invol)
Definition: vol.c:48
#define MSG_0
Definition: ui.h:12
char name[80]
Definition: animskull.c:25
Tachyon public API function prototypes and declarations used to drive the ray tracing engine...
int yres
Definition: animskull.c:22
apiflt opacity
Definition: animskull.c:24
void box_normal(const box *bx, const vector *pnt, const ray *incident, vector *N)
Definition: box.c:103
unsigned char * data
Definition: animskull.c:26
#define FABS(x)
Definition: util.h:28
int scalarvol_bbox(void *obj, vector *min, vector *max)
Definition: vol.c:30
color constant_texture(const vector *hit, const texture *tx, const ray *ry)
Definition: texture.c:149
axis-aligned box definition
Definition: box.h:14