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