c56d17f7cc387dd5643e03b69f57deb7b02bed30
[blender.git] / source / blender / render / intern / source / volume_precache.c
1 /**
2  *
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): Matt Ebb.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <math.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <float.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BLI_blenlib.h"
37 #include "BLI_arithb.h"
38
39 #include "PIL_time.h"
40
41 #include "RE_shader_ext.h"
42 #include "RE_raytrace.h"
43
44 #include "DNA_material_types.h"
45
46 #include "render_types.h"
47 #include "renderdatabase.h"
48 #include "volumetric.h"
49
50 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
51 /* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
52 /* only to be used here in this file, it's for speed */
53 extern struct Render R;
54 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
55
56
57 /* Recursive test for intersections, from a point inside the mesh, to outside
58  * Number of intersections (depth) determine if a point is inside or outside the mesh */
59 int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int limit, int depth)
60 {
61         if (limit == 0) return depth;
62         
63         if (RE_ray_tree_intersect(tree, isect)) {
64                 float hitco[3];
65                 
66                 hitco[0] = isect->start[0] + isect->labda*isect->vec[0];
67                 hitco[1] = isect->start[1] + isect->labda*isect->vec[1];
68                 hitco[2] = isect->start[2] + isect->labda*isect->vec[2];
69                 VecAddf(isect->start, hitco, offset);
70
71                 return intersect_outside_volume(tree, isect, offset, limit-1, depth+1);
72         } else {
73                 return depth;
74         }
75 }
76
77 /* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */
78 int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co)
79 {
80         float maxsize = RE_ray_tree_max_size(tree);
81         Isect isect;
82         float vec[3] = {0.0f,0.0f,1.0f};
83         int final_depth=0, depth=0, limit=20;
84         
85         /* set up the isect */
86         memset(&isect, 0, sizeof(isect));
87         VECCOPY(isect.start, co);
88         isect.end[0] = co[0] + vec[0] * maxsize;
89         isect.end[1] = co[1] + vec[1] * maxsize;
90         isect.end[2] = co[2] + vec[2] * maxsize;
91         
92         /* and give it a little offset to prevent self-intersections */
93         VecMulf(vec, 1e-5);
94         VecAddf(isect.start, isect.start, vec);
95         
96         isect.mode= RE_RAY_MIRROR;
97         isect.face_last= NULL;
98         isect.lay= -1;
99         
100         final_depth = intersect_outside_volume(tree, &isect, vec, limit, depth);
101         
102         /* even number of intersections: point is outside
103          * odd number: point is inside */
104         if (final_depth % 2 == 0) return 0;
105         else return 1;
106 }
107
108 static int inside_check_func(Isect *is, int ob, RayFace *face)
109 {
110         return 1;
111 }
112 static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4)
113 {
114         VlakRen *vlr= (VlakRen*)face;
115
116         *v1 = (vlr->v1)? vlr->v1->co: NULL;
117         *v2 = (vlr->v2)? vlr->v2->co: NULL;
118         *v3 = (vlr->v3)? vlr->v3->co: NULL;
119         *v4 = (vlr->v4)? vlr->v4->co: NULL;
120 }
121
122 RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax)
123 {
124         int v;
125         VlakRen *vlr= NULL;
126         
127         /* create empty raytree */
128         RayTree *tree = RE_ray_tree_create(64, obi->obr->totvlak, bbmin, bbmax,
129                 vlr_face_coords, inside_check_func, NULL, NULL);
130         
131         /* fill it with faces */
132         for(v=0; v<obi->obr->totvlak; v++) {
133                 if((v & 255)==0)
134                         vlr= obi->obr->vlaknodes[v>>8].vlak;
135                 else
136                         vlr++;
137         
138                 RE_ray_tree_add_face(tree, 0, vlr);
139         }
140         
141         RE_ray_tree_done(tree);
142         
143         return tree;
144 }
145
146 static float get_avg_surrounds(float *cache, int res, int res_2, int res_3, int rgb, int xx, int yy, int zz)
147 {
148         int x, y, z, x_, y_, z_;
149         int added=0;
150         float tot=0.0f;
151         int i;
152         
153         for (x=-1; x <= 1; x++) {
154                 x_ = xx+x;
155                 if (x_ >= 0 && x_ <= res-1) {
156                 
157                         for (y=-1; y <= 1; y++) {
158                                 y_ = yy+y;
159                                 if (y_ >= 0 && y_ <= res-1) {
160                                 
161                                         for (z=-1; z <= 1; z++) {
162                                                 z_ = zz+z;
163                                                 if (z_ >= 0 && z_ <= res-1) {
164                                                 
165                                                         i = rgb*res_3 + x_*res_2 + y_*res + z_;
166                                                         if (cache[i] > 0.0f) {
167                                                                 tot += cache[i];
168                                                                 added++;
169                                                         }
170                                                         
171                                                 }
172                                         }
173                                 }
174                         }
175                 }
176         }
177         
178         tot /= added;
179         
180         return ((added>0)?tot:0.0f);
181 }
182
183 /* function to filter the edges of the light cache, where there was no volume originally.
184  * For each voxel which was originally external to the mesh, it finds the average values of
185  * the surrounding internal voxels and sets the original external voxel to that average amount.
186  * Works almost a bit like a 'dilate' filter */
187 static void lightcache_filter(float *cache, int res)
188 {
189         int x, y, z, rgb;
190         int res_2, res_3;
191         int i;
192         
193         res_2 = res*res;
194         res_3 = res*res*res;
195
196         for (x=0; x < res; x++) {
197                 for (y=0; y < res; y++) {
198                         for (z=0; z < res; z++) {
199                                 for (rgb=0; rgb < 3; rgb++) {
200                                         i = rgb*res_3 + x*res_2 + y*res + z;
201
202                                         /* trigger for outside mesh */
203                                         if (cache[i] < 0.5f) cache[i] = get_avg_surrounds(cache, res, res_2, res_3, rgb, x, y, z);
204                                 }
205                         }
206                 }
207         }
208 }
209
210 /* Precache a volume into a 3D voxel grid.
211  * The voxel grid is stored in the ObjectInstanceRen, 
212  * in camera space, aligned with the ObjectRen's bounding box.
213  * Resolution is defined by the user.
214  */
215 void vol_precache_objectinstance(Render *re, ObjectInstanceRen *obi, Material *ma, float *bbmin, float *bbmax)
216 {
217         int x, y, z;
218
219         float co[3], voxel[3], scatter_col[3];
220         ShadeInput shi;
221         float view[3] = {0.0,0.0,-1.0};
222         float density;
223         float stepsize;
224         
225         float resf, res_3f;
226         int res_2, res_3;
227         
228         float i = 1.0f;
229         double time, lasttime= PIL_check_seconds_timer();
230         const int res = ma->vol_precache_resolution;
231         RayTree *tree;
232         
233         R = *re;
234
235         /* create a raytree with just the faces of the instanced ObjectRen, 
236          * used for checking if the cached point is inside or outside. */
237         tree = create_raytree_obi(obi, bbmin, bbmax);
238         if (!tree) return;
239
240         /* Need a shadeinput to calculate scattering */
241         memset(&shi, 0, sizeof(ShadeInput)); 
242         shi.depth= 1;
243         shi.mask= 1;
244         shi.mat = ma;
245         shi.vlr = NULL;
246         memcpy(&shi.r, &shi.mat->r, 23*sizeof(float));  // note, keep this synced with render_types.h
247         shi.har= shi.mat->har;
248         shi.obi= obi;
249         shi.obr= obi->obr;
250         shi.lay = re->scene->lay;
251         VECCOPY(shi.view, view);
252         
253         stepsize = vol_get_stepsize(&shi, STEPSIZE_VIEW);
254
255         resf = (float)res;
256         res_2 = res*res;
257         res_3 = res*res*res;
258         res_3f = (float)res_3;
259         
260         VecSubf(voxel, bbmax, bbmin);
261         if ((voxel[0] < FLT_EPSILON) || (voxel[1] < FLT_EPSILON) || (voxel[2] < FLT_EPSILON))
262                 return;
263         VecMulf(voxel, 1.0f/res);
264         
265         obi->volume_precache = MEM_callocN(sizeof(float)*res_3*3, "volume light cache");
266         
267         /* Iterate over the 3d voxel grid, and fill the voxels with scattering information
268          *
269          * It's stored in memory as 3 big float grids next to each other, one for each RGB channel.
270          * I'm guessing the memory alignment may work out better this way for the purposes
271          * of doing linear interpolation, but I haven't actually tested this theory! :)
272          */
273         for (x=0; x < res; x++) {
274                 co[0] = bbmin[0] + (voxel[0] * x);
275                 
276                 for (y=0; y < res; y++) {
277                         co[1] = bbmin[1] + (voxel[1] * y);
278                         
279                         for (z=0; z < res; z++) {
280                                 co[2] = bbmin[2] + (voxel[2] * z);
281                         
282                                 time= PIL_check_seconds_timer();
283                                 i++;
284                         
285                                 /* display progress every second */
286                                 if(re->test_break()) {
287                                         if(tree) {
288                                                 RE_ray_tree_free(tree);
289                                                 tree= NULL;
290                                         }
291                                         return;
292                                 }
293                                 if(time-lasttime>1.0f) {
294                                         char str[64];
295                                         sprintf(str, "Precaching volume: %d%%", (int)(100.0f * (i / res_3f)));
296                                         re->i.infostr= str;
297                                         re->stats_draw(&re->i);
298                                         re->i.infostr= NULL;
299                                         lasttime= time;
300                                 }
301                                 
302                                 /* don't bother if the point is not inside the volume mesh */
303                                 if (!point_inside_obi(tree, obi, co)) {
304                                         obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = -1.0f;
305                                         obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = -1.0f;
306                                         obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = -1.0f;
307                                         continue;
308                                 }
309                                 density = vol_get_density(&shi, co);
310                                 vol_get_scattering(&shi, scatter_col, co, stepsize, density);
311                         
312                                 obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = scatter_col[0];
313                                 obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = scatter_col[1];
314                                 obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = scatter_col[2];
315                         }
316                 }
317         }
318
319         if(tree) {
320                 RE_ray_tree_free(tree);
321                 tree= NULL;
322         }
323         
324         lightcache_filter(obi->volume_precache, res);
325
326 }
327
328 #if 0
329 void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma, float *bbmin, float *bbmax)
330 {
331         int x, y, z;
332
333         float co[3], voxel[3], scatter_col[3];
334         ShadeInput shi;
335         float view[3] = {0.0,0.0,-1.0};
336         float density;
337         float stepsize;
338         
339         float resf, res_3f;
340         int res_2, res_3;
341         
342         int edgeparts=2;
343         int totparts;
344         
345         float i = 1.0f;
346         double time, lasttime= PIL_check_seconds_timer();
347         const int res = ma->vol_precache_resolution;
348         RayTree *tree;
349         
350         R = *re;
351
352         /* create a raytree with just the faces of the instanced ObjectRen, 
353          * used for checking if the cached point is inside or outside. */
354         tree = create_raytree_obi(obi, bbmin, bbmax);
355         if (!tree) return;
356
357         /* Need a shadeinput to calculate scattering */
358         memset(&shi, 0, sizeof(ShadeInput)); 
359         shi.depth= 1;
360         shi.mask= 1;
361         shi.mat = ma;
362         shi.vlr = NULL;
363         memcpy(&shi.r, &shi.mat->r, 23*sizeof(float));  // note, keep this synced with render_types.h
364         shi.har= shi.mat->har;
365         shi.obi= obi;
366         shi.obr= obi->obr;
367         shi.lay = re->scene->lay;
368         VECCOPY(shi.view, view);
369         
370         stepsize = vol_get_stepsize(&shi, STEPSIZE_VIEW);
371
372         resf = (float)res;
373         res_2 = res*res;
374         res_3 = res*res*res;
375         res_3f = (float)res_3;
376         
377         VecSubf(voxel, bbmax, bbmin);
378         if ((voxel[0] < FLT_EPSILON) || (voxel[1] < FLT_EPSILON) || (voxel[2] < FLT_EPSILON))
379                 return;
380         VecMulf(voxel, 1.0f/res);
381         
382         
383         part[0] = parceil(res/(float)xparts); 
384         part[1] = ceil(rex/(float)yparts);
385         part[2] = ceil(rex/(float)zparts);
386         
387         
388         
389         //obi->volume_precache = MEM_callocN(sizeof(float)*res_3*3, "volume light cache");
390         
391         /* Iterate over the 3d voxel grid, and fill the voxels with scattering information
392          *
393          * It's stored in memory as 3 big float grids next to each other, one for each RGB channel.
394          * I'm guessing the memory alignment may work out better this way for the purposes
395          * of doing linear interpolation, but I haven't actually tested this theory! :)
396          */
397          /*
398         for (x=0; x < res; x++) {
399                 co[0] = bbmin[0] + (voxel[0] * x);
400                 
401                 for (y=0; y < res; y++) {
402                         co[1] = bbmin[1] + (voxel[1] * y);
403                         
404                         for (z=0; z < res; z++) {
405                                 co[2] = bbmin[2] + (voxel[2] * z);
406                         
407                                 time= PIL_check_seconds_timer();
408                                 i++;
409                         
410                                 // display progress every second
411                                 if(re->test_break()) {
412                                         if(tree) {
413                                                 RE_ray_tree_free(tree);
414                                                 tree= NULL;
415                                         }
416                                         return;
417                                 }
418                                 if(time-lasttime>1.0f) {
419                                         char str[64];
420                                         sprintf(str, "Precaching volume: %d%%", (int)(100.0f * (i / res_3f)));
421                                         re->i.infostr= str;
422                                         re->stats_draw(&re->i);
423                                         re->i.infostr= NULL;
424                                         lasttime= time;
425                                 }
426                                 
427                                 // don't bother if the point is not inside the volume mesh
428                                 
429                                 if (!point_inside_obi(tree, obi, co)) {
430                                         obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = -1.0f;
431                                         obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = -1.0f;
432                                         obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = -1.0f;
433                                         continue;
434                                 }
435                                 density = vol_get_density(&shi, co);
436                                 vol_get_scattering(&shi, scatter_col, co, stepsize, density);
437                         
438                                 obi->volume_precache[0*res_3 + x*res_2 + y*res + z] = scatter_col[0];
439                                 obi->volume_precache[1*res_3 + x*res_2 + y*res + z] = scatter_col[1];
440                                 obi->volume_precache[2*res_3 + x*res_2 + y*res + z] = scatter_col[2];
441                                 
442                         }
443                 }
444         }
445         */
446
447         if(tree) {
448                 RE_ray_tree_free(tree);
449                 tree= NULL;
450         }
451         
452         lightcache_filter(obi->volume_precache, res);
453
454 }
455 #endif
456
457 /* loop through all objects (and their associated materials)
458  * marked for pre-caching in convertblender.c, and pre-cache them */
459 void volume_precache(Render *re)
460 {
461         ObjectInstanceRen *obi;
462         VolPrecache *vp;
463
464         for(vp= re->vol_precache_obs.first; vp; vp= vp->next) {
465                 for(obi= re->instancetable.first; obi; obi= obi->next) {
466                         if (obi->obr == vp->obr) 
467                                 vol_precache_objectinstance(re, obi, vp->ma, obi->obr->boundbox[0], obi->obr->boundbox[1]);
468                 }
469         }
470         
471         re->i.infostr= NULL;
472         re->stats_draw(&re->i);
473 }
474
475 void free_volume_precache(Render *re)
476 {
477         ObjectInstanceRen *obi;
478         
479         for(obi= re->instancetable.first; obi; obi= obi->next) {
480                 if (obi->volume_precache)
481                         MEM_freeN(obi->volume_precache);
482         }
483         
484         BLI_freelistN(&re->vol_precache_obs);
485 }