Merge with trunk r41197
[blender.git] / source / blender / render / intern / source / voxeldata.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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): Raul Fernandez Hernandez (Farsthary), Matt Ebb.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file blender/render/intern/source/voxeldata.c
30  *  \ingroup render
31  */
32
33
34 #include <math.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_math.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_voxel.h"
43 #include "BLI_utildefines.h"
44
45 #include "IMB_imbuf.h"
46 #include "IMB_imbuf_types.h"
47
48 #include "BKE_global.h"
49 #include "BKE_image.h"
50 #include "BKE_main.h"
51 #include "BKE_modifier.h"
52
53 #include "smoke_API.h"
54
55 #include "DNA_texture_types.h"
56 #include "DNA_object_force.h"
57 #include "DNA_object_types.h"
58 #include "DNA_modifier_types.h"
59 #include "DNA_smoke_types.h"
60
61
62 #include "render_types.h"
63 #include "renderdatabase.h"
64 #include "texture.h"
65 #include "voxeldata.h"
66
67 static int is_vd_res_ok(VoxelData *vd)
68 {
69         /* arbitrary large value so corrupt headers dont break */
70         const int min= 1, max= 100000;
71         return  (vd->resol[0] >= min && vd->resol[0] <= max) &&
72                         (vd->resol[1] >= min && vd->resol[1] <= max) &&
73                         (vd->resol[2] >= min && vd->resol[2] <= max);
74 }
75
76 /* use size_t because the result may exceed INT_MAX */
77 static size_t vd_resol_size(VoxelData *vd)
78 {
79         return (size_t)vd->resol[0] * (size_t)vd->resol[1] * (size_t)vd->resol[2];
80 }
81
82 static int load_frame_blendervoxel(VoxelData *vd, FILE *fp, int frame)
83 {       
84         const size_t size = vd_resol_size(vd);
85         size_t offset = sizeof(VoxelDataHeader);
86         
87         if(is_vd_res_ok(vd) == FALSE)
88                 return 0;
89
90         vd->dataset = MEM_mapallocN(sizeof(float)*size, "voxel dataset");
91         if(vd->dataset == NULL) return 0;
92
93         if(fseek(fp, frame*size*sizeof(float)+offset, 0) == -1)
94                 return 0;
95         if(fread(vd->dataset, sizeof(float), size, fp) != size)
96                 return 0;
97         
98         vd->cachedframe = frame;
99         vd->ok = 1;
100         return 1;
101 }
102
103 static int load_frame_raw8(VoxelData *vd, FILE *fp, int frame)
104 {
105         const size_t size = vd_resol_size(vd);
106         char *data_c;
107         int i;
108
109         if(is_vd_res_ok(vd) == FALSE)
110                 return 0;
111
112         vd->dataset = MEM_mapallocN(sizeof(float)*size, "voxel dataset");
113         if(vd->dataset == NULL) return 0;
114         data_c = (char *)MEM_mallocN(sizeof(char)*size, "temporary voxel file reading storage");
115         if(data_c == NULL) {
116                 MEM_freeN(vd->dataset);
117                 vd->dataset= NULL;
118                 return 0;
119         }
120
121         if(fseek(fp,(frame-1)*size*sizeof(char),0) == -1) {
122                 MEM_freeN(data_c);
123                 MEM_freeN(vd->dataset);
124                 vd->dataset= NULL;
125                 return 0;
126         }
127         if(fread(data_c, sizeof(char), size, fp) != size) {
128                 MEM_freeN(data_c);
129                 MEM_freeN(vd->dataset);
130                 vd->dataset= NULL;
131                 return 0;
132         }
133         
134         for (i=0; i<size; i++) {
135                 vd->dataset[i] = (float)data_c[i] / 255.f;
136         }
137         MEM_freeN(data_c);
138         
139         vd->cachedframe = frame;
140         vd->ok = 1;
141         return 1;
142 }
143
144 static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
145 {
146         ImBuf *ibuf;
147         Image *ima = tex->ima;
148         ImageUser *tiuser = &tex->iuser;
149         ImageUser iuser = *(tiuser);
150         int x=0, y=0, z=0;
151         float *rf;
152
153         if (!ima || !tiuser) return;
154         if (iuser.frames == 0) return;
155         
156         ima->source = IMA_SRC_SEQUENCE;
157         iuser.framenr = 1 + iuser.offset;
158
159         /* find the first valid ibuf and use it to initialise the resolution of the data set */
160         /* need to do this in advance so we know how much memory to allocate */
161         ibuf= BKE_image_get_ibuf(ima, &iuser);
162         while (!ibuf && (iuser.framenr < iuser.frames)) {
163                 iuser.framenr++;
164                 ibuf= BKE_image_get_ibuf(ima, &iuser);
165         }
166         if (!ibuf) return;
167         if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
168         
169         vd->flag |= TEX_VD_STILL;
170         vd->resol[0] = ibuf->x;
171         vd->resol[1] = ibuf->y;
172         vd->resol[2] = iuser.frames;
173         vd->dataset = MEM_mapallocN(sizeof(float)*vd_resol_size(vd), "voxel dataset");
174         
175         for (z=0; z < iuser.frames; z++)
176         {       
177                 /* get a new ibuf for each frame */
178                 if (z > 0) {
179                         iuser.framenr++;
180                         ibuf= BKE_image_get_ibuf(ima, &iuser);
181                         if (!ibuf) break;
182                         if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
183                 }
184                 rf = ibuf->rect_float;
185                 
186                 for (y=0; y < ibuf->y; y++)
187                 {
188                         for (x=0; x < ibuf->x; x++)
189                         {
190                                 /* currently averaged to monchrome */
191                                 vd->dataset[ V_I(x, y, z, vd->resol) ] = (rf[0] + rf[1] + rf[2])*0.333f;
192                                 rf +=4;
193                         }
194                 }
195                 
196                 BKE_image_free_anim_ibufs(ima, iuser.framenr);
197         }
198         
199         vd->ok = 1;
200         return;
201 }
202
203 static int read_voxeldata_header(FILE *fp, struct VoxelData *vd)
204 {
205         VoxelDataHeader *h=(VoxelDataHeader *)MEM_mallocN(sizeof(VoxelDataHeader), "voxel data header");
206         
207         rewind(fp);
208         if(fread(h,sizeof(VoxelDataHeader),1,fp) != 1) {
209                 MEM_freeN(h);
210                 return 0;
211         }
212         
213         vd->resol[0]=h->resolX;
214         vd->resol[1]=h->resolY;
215         vd->resol[2]=h->resolZ;
216
217         MEM_freeN(h);
218         return 1;
219 }
220
221 static void init_frame_smoke(VoxelData *vd, float cfra)
222 {
223 #ifdef WITH_SMOKE
224         Object *ob;
225         ModifierData *md;
226         
227         vd->dataset = NULL;
228         if (vd->object == NULL) return; 
229         ob= vd->object;
230         
231         /* draw code for smoke */
232         if( (md = (ModifierData *)modifiers_findByType(ob, eModifierType_Smoke)) )
233         {
234                 SmokeModifierData *smd = (SmokeModifierData *)md;
235
236                 
237                 if(smd->domain && smd->domain->fluid) {
238                         if(cfra < smd->domain->point_cache[0]->startframe)
239                                 ; /* don't show smoke before simulation starts, this could be made an option in the future */
240                         else if (vd->smoked_type == TEX_VD_SMOKEHEAT) {
241                                 size_t totRes;
242                                 size_t i;
243                                 float *heat;
244
245                                 VECCOPY(vd->resol, smd->domain->res);
246                                 totRes= vd_resol_size(vd);
247
248                                 // scaling heat values from -2.0-2.0 to 0.0-1.0
249                                 vd->dataset = MEM_mapallocN(sizeof(float)*(totRes), "smoke data");
250
251
252                                 heat = smoke_get_heat(smd->domain->fluid);
253
254                                 for (i=0; i<totRes; i++)
255                                 {
256                                         vd->dataset[i] = (heat[i]+2.0f)/4.0f;
257                                 }
258
259                                 //vd->dataset = smoke_get_heat(smd->domain->fluid);
260                         }
261                         else if (vd->smoked_type == TEX_VD_SMOKEVEL) {
262                                 size_t totRes;
263                                 size_t i;
264                                 float *xvel, *yvel, *zvel;
265
266                                 VECCOPY(vd->resol, smd->domain->res);
267                                 totRes= vd_resol_size(vd);
268
269                                 // scaling heat values from -2.0-2.0 to 0.0-1.0
270                                 vd->dataset = MEM_mapallocN(sizeof(float)*(totRes), "smoke data");
271
272                                 xvel = smoke_get_velocity_x(smd->domain->fluid);
273                                 yvel = smoke_get_velocity_y(smd->domain->fluid);
274                                 zvel = smoke_get_velocity_z(smd->domain->fluid);
275
276                                 for (i=0; i<totRes; i++)
277                                 {
278                                         vd->dataset[i] = sqrt(xvel[i]*xvel[i] + yvel[i]*yvel[i] + zvel[i]*zvel[i])*3.0f;
279                                 }
280
281                         }
282                         else {
283                                 size_t totRes;
284                                 float *density;
285
286                                 if (smd->domain->flags & MOD_SMOKE_HIGHRES) {
287                                         smoke_turbulence_get_res(smd->domain->wt, vd->resol);
288                                         density = smoke_turbulence_get_density(smd->domain->wt);
289                                 } else {
290                                         VECCOPY(vd->resol, smd->domain->res);
291                                         density = smoke_get_density(smd->domain->fluid);
292                                 }
293
294                                 /* TODO: is_vd_res_ok(rvd) doesnt check this resolution */
295                                 totRes= vd_resol_size(vd);
296                                 /* always store copy, as smoke internal data can change */
297                                 vd->dataset = MEM_mapallocN(sizeof(float)*(totRes), "smoke data");
298                                 memcpy(vd->dataset, density, sizeof(float)*totRes);
299                         } // end of fluid condition
300                 }
301         }
302         
303         vd->ok = 1;
304
305 #else // WITH_SMOKE
306         (void)vd;
307         (void)cfra;
308
309         vd->dataset= NULL;
310 #endif
311 }
312
313 void cache_voxeldata(Tex *tex, int scene_frame)
314 {       
315         VoxelData *vd = tex->vd;
316         FILE *fp;
317         int curframe;
318         char path[sizeof(vd->source_path)];
319         
320         /* only re-cache if dataset needs updating */
321         if ((vd->flag & TEX_VD_STILL) || (vd->cachedframe == scene_frame))
322                 if (vd->ok) return;
323         
324         /* clear out old cache, ready for new */
325         if (vd->dataset) {
326                 MEM_freeN(vd->dataset);
327                 vd->dataset = NULL;
328         }
329
330         if (vd->flag & TEX_VD_STILL)
331                 curframe = vd->still_frame;
332         else
333                 curframe = scene_frame;
334         
335         BLI_strncpy(path, vd->source_path, sizeof(path));
336         
337         switch(vd->file_format) {
338                 case TEX_VD_IMAGE_SEQUENCE:
339                         load_frame_image_sequence(vd, tex);
340                         return;
341                 case TEX_VD_SMOKE:
342                         init_frame_smoke(vd, scene_frame);
343                         return;
344                 case TEX_VD_BLENDERVOXEL:
345                         BLI_path_abs(path, G.main->name);
346                         if (!BLI_exists(path)) return;
347                         fp = fopen(path,"rb");
348                         if (!fp) return;
349                         
350                         if(read_voxeldata_header(fp, vd))
351                                 load_frame_blendervoxel(vd, fp, curframe-1);
352
353                         fclose(fp);
354                         return;
355                 case TEX_VD_RAW_8BIT:
356                         BLI_path_abs(path, G.main->name);
357                         if (!BLI_exists(path)) return;
358                         fp = fopen(path,"rb");
359                         if (!fp) return;
360                         
361                         load_frame_raw8(vd, fp, curframe);
362                         fclose(fp);
363                         return;
364         }
365 }
366
367 void make_voxeldata(struct Render *re)
368 {
369         Tex *tex;
370         
371         re->i.infostr= "Loading voxel datasets";
372         re->stats_draw(re->sdh, &re->i);
373         
374         /* XXX: should be doing only textures used in this render */
375         for (tex= re->main->tex.first; tex; tex= tex->id.next) {
376                 if(tex->id.us && tex->type==TEX_VOXELDATA) {
377                         cache_voxeldata(tex, re->r.cfra);
378                 }
379         }
380         
381         re->i.infostr= NULL;
382         re->stats_draw(re->sdh, &re->i);
383         
384 }
385
386 int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texres)
387 {        
388         int retval = TEX_INT;
389         VoxelData *vd = tex->vd;        
390         float co[3], offset[3] = {0.5, 0.5, 0.5};
391
392         if (vd->dataset==NULL) {
393                 texres->tin = 0.0f;
394                 return 0;
395         }
396         
397         /* scale lookup from 0.0-1.0 (original location) to -1.0, 1.0, consistent with image texture tex coords */
398         /* in implementation this works backwards, bringing sample locations from -1.0, 1.0
399          * to the range 0.0, 1.0, before looking up in the voxel structure. */
400         copy_v3_v3(co, texvec);
401         mul_v3_fl(co, 0.5f);
402         add_v3_v3(co, offset);
403
404         /* co is now in the range 0.0, 1.0 */
405         switch (vd->extend) {
406                 case TEX_CLIP:
407                 {
408                         if ((co[0] < 0.f || co[0] > 1.f) || (co[1] < 0.f || co[1] > 1.f) || (co[2] < 0.f || co[2] > 1.f)) {
409                                 texres->tin = 0.f;
410                                 return retval;
411                         }
412                         break;
413                 }
414                 case TEX_REPEAT:
415                 {
416                         co[0] = co[0] - floorf(co[0]);
417                         co[1] = co[1] - floorf(co[1]);
418                         co[2] = co[2] - floorf(co[2]);
419                         break;
420                 }
421                 case TEX_EXTEND:
422                 {
423                         CLAMP(co[0], 0.f, 1.f);
424                         CLAMP(co[1], 0.f, 1.f);
425                         CLAMP(co[2], 0.f, 1.f);
426                         break;
427                 }
428         }
429         
430         switch (vd->interp_type) {
431                 case TEX_VD_NEARESTNEIGHBOR:
432                         texres->tin = voxel_sample_nearest(vd->dataset, vd->resol, co);
433                         break;  
434                 case TEX_VD_LINEAR:
435                         texres->tin = voxel_sample_trilinear(vd->dataset, vd->resol, co);
436                         break;                                  
437                 case TEX_VD_QUADRATIC:
438                         texres->tin = voxel_sample_triquadratic(vd->dataset, vd->resol, co);
439                         break;
440                 case TEX_VD_TRICUBIC_CATROM:
441                 case TEX_VD_TRICUBIC_BSPLINE:
442                         texres->tin = voxel_sample_tricubic(vd->dataset, vd->resol, co, (vd->interp_type == TEX_VD_TRICUBIC_BSPLINE));
443                         break;
444         }
445         
446         texres->tin *= vd->int_multiplier;
447         BRICONT;
448         
449         texres->tr = texres->tin;
450         texres->tg = texres->tin;
451         texres->tb = texres->tin;
452         texres->ta = texres->tin;
453         BRICONTRGB;
454         
455         return retval;  
456 }
457
458