Code cleanup: remove TRUE/FALSE & WITH_BOOL_COMPAT define
[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 #ifdef WIN32
38 #include "BLI_winstuff.h"
39 #endif
40
41 #include "MEM_guardedalloc.h"
42
43 #include "BLI_math.h"
44 #include "BLI_blenlib.h"
45 #include "BLI_threads.h"
46 #include "BLI_voxel.h"
47 #include "BLI_utildefines.h"
48
49 #include "BLF_translation.h"
50
51 #include "IMB_imbuf.h"
52 #include "IMB_imbuf_types.h"
53
54 #include "BKE_global.h"
55 #include "BKE_image.h"
56 #include "BKE_main.h"
57 #include "BKE_modifier.h"
58
59 #include "smoke_API.h"
60
61 #include "DNA_texture_types.h"
62 #include "DNA_object_force.h"
63 #include "DNA_object_types.h"
64 #include "DNA_modifier_types.h"
65 #include "DNA_smoke_types.h"
66
67
68 #include "render_types.h"
69 #include "renderdatabase.h"
70 #include "texture.h"
71 #include "voxeldata.h"
72
73 static bool is_vd_res_ok(VoxelData *vd)
74 {
75         /* arbitrary large value so corrupt headers don't break */
76         const int min = 1, max = 100000;
77         return (vd->resol[0] >= min && vd->resol[0] <= max) &&
78                (vd->resol[1] >= min && vd->resol[1] <= max) &&
79                (vd->resol[2] >= min && vd->resol[2] <= max);
80 }
81
82 /* use size_t because the result may exceed INT_MAX */
83 static size_t vd_resol_size(VoxelData *vd)
84 {
85         return (size_t)vd->resol[0] * (size_t)vd->resol[1] * (size_t)vd->resol[2];
86 }
87
88 static int load_frame_blendervoxel(VoxelData *vd, FILE *fp, int frame)
89 {       
90         const size_t size = vd_resol_size(vd);
91         size_t offset = sizeof(VoxelDataHeader);
92         
93         if (is_vd_res_ok(vd) == false)
94                 return 0;
95
96         vd->dataset = MEM_mapallocN(sizeof(float) * size, "voxel dataset");
97         if (vd->dataset == NULL) return 0;
98
99         if (fseek(fp, frame * size * sizeof(float) + offset, 0) == -1)
100                 return 0;
101         if (fread(vd->dataset, sizeof(float), size, fp) != size)
102                 return 0;
103         
104         vd->cachedframe = frame;
105         vd->ok = 1;
106         return 1;
107 }
108
109 static int load_frame_raw8(VoxelData *vd, FILE *fp, int frame)
110 {
111         const size_t size = vd_resol_size(vd);
112         size_t i;
113         char *data_c;
114
115         if (is_vd_res_ok(vd) == false)
116                 return 0;
117
118         vd->dataset = MEM_mapallocN(sizeof(float) * size, "voxel dataset");
119         if (vd->dataset == NULL) return 0;
120         data_c = (char *)MEM_mallocN(sizeof(char) * size, "temporary voxel file reading storage");
121         if (data_c == NULL) {
122                 MEM_freeN(vd->dataset);
123                 vd->dataset = NULL;
124                 return 0;
125         }
126
127         if (fseek(fp, (frame - 1) * size * sizeof(char), 0) == -1) {
128                 MEM_freeN(data_c);
129                 MEM_freeN(vd->dataset);
130                 vd->dataset = NULL;
131                 return 0;
132         }
133         if (fread(data_c, sizeof(char), size, fp) != size) {
134                 MEM_freeN(data_c);
135                 MEM_freeN(vd->dataset);
136                 vd->dataset = NULL;
137                 return 0;
138         }
139         
140         for (i = 0; i < size; i++) {
141                 vd->dataset[i] = (float)data_c[i] / 255.f;
142         }
143         MEM_freeN(data_c);
144         
145         vd->cachedframe = frame;
146         vd->ok = 1;
147         return 1;
148 }
149
150 static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
151 {
152         ImBuf *ibuf;
153         Image *ima = tex->ima;
154         ImageUser *tiuser = &tex->iuser;
155         ImageUser iuser = *(tiuser);
156         int x = 0, y = 0, z = 0;
157         float *rf;
158
159         if (!ima) return;
160         if (iuser.frames == 0) return;
161         
162         ima->source = IMA_SRC_SEQUENCE;
163         iuser.framenr = 1 + iuser.offset;
164
165         /* find the first valid ibuf and use it to initialize the resolution of the data set */
166         /* need to do this in advance so we know how much memory to allocate */
167         ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
168         while (!ibuf && (iuser.framenr < iuser.frames)) {
169                 iuser.framenr++;
170                 ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
171         }
172         if (!ibuf) return;
173         if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
174         
175         vd->flag |= TEX_VD_STILL;
176         vd->resol[0] = ibuf->x;
177         vd->resol[1] = ibuf->y;
178         vd->resol[2] = iuser.frames;
179         vd->dataset = MEM_mapallocN(sizeof(float) * vd_resol_size(vd), "voxel dataset");
180         
181         for (z = 0; z < iuser.frames; z++) {
182                 /* get a new ibuf for each frame */
183                 if (z > 0) {
184                         iuser.framenr++;
185                         BKE_image_release_ibuf(ima, ibuf, NULL);
186                         ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
187                         if (!ibuf) break;
188                         if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
189                 }
190                 rf = ibuf->rect_float;
191                 
192                 for (y = 0; y < ibuf->y; y++) {
193                         for (x = 0; x < ibuf->x; x++) {
194                                 /* currently averaged to monchrome */
195                                 vd->dataset[BLI_VOXEL_INDEX(x, y, z, vd->resol)] = (rf[0] + rf[1] + rf[2]) / 3.0f;
196                                 rf += 4;
197                         }
198                 }
199                 
200                 BKE_image_free_anim_ibufs(ima, iuser.framenr);
201         }
202
203         BKE_image_release_ibuf(ima, ibuf, NULL);
204
205         vd->ok = 1;
206         return;
207 }
208
209 static int read_voxeldata_header(FILE *fp, struct VoxelData *vd)
210 {
211         VoxelDataHeader *h = (VoxelDataHeader *)MEM_mallocN(sizeof(VoxelDataHeader), "voxel data header");
212         
213         rewind(fp);
214         if (fread(h, sizeof(VoxelDataHeader), 1, fp) != 1) {
215                 MEM_freeN(h);
216                 return 0;
217         }
218         
219         vd->resol[0] = h->resolX;
220         vd->resol[1] = h->resolY;
221         vd->resol[2] = h->resolZ;
222
223         MEM_freeN(h);
224         return 1;
225 }
226
227 static void init_frame_smoke(VoxelData *vd, int cfra)
228 {
229 #ifdef WITH_SMOKE
230         Object *ob;
231         ModifierData *md;
232         
233         vd->dataset = NULL;
234         if (vd->object == NULL) return;
235         ob = vd->object;
236         
237         /* draw code for smoke */
238         if ((md = (ModifierData *)modifiers_findByType(ob, eModifierType_Smoke))) {
239                 SmokeModifierData *smd = (SmokeModifierData *)md;
240                 SmokeDomainSettings *sds = smd->domain;
241                 
242                 if (sds && sds->fluid) {
243                         BLI_rw_mutex_lock(sds->fluid_mutex, THREAD_LOCK_READ);
244
245                         if (!sds->fluid) {
246                                 BLI_rw_mutex_unlock(sds->fluid_mutex);
247                                 return;
248                         }
249
250                         if (cfra < sds->point_cache[0]->startframe)
251                                 ;  /* don't show smoke before simulation starts, this could be made an option in the future */
252                         else if (vd->smoked_type == TEX_VD_SMOKEHEAT) {
253                                 size_t totRes;
254                                 size_t i;
255                                 float *heat;
256
257                                 if (!smoke_has_heat(sds->fluid)) {
258                                         BLI_rw_mutex_unlock(sds->fluid_mutex);
259                                         return;
260                                 }
261
262                                 copy_v3_v3_int(vd->resol, sds->res);
263                                 totRes = vd_resol_size(vd);
264                                 vd->dataset = MEM_mapallocN(sizeof(float) * (totRes), "smoke data");
265                                 /* get heat data */
266                                 heat = smoke_get_heat(sds->fluid);
267
268                                 /* scale heat values from -2.0-2.0 to 0.0-1.0 */
269                                 for (i = 0; i < totRes; i++) {
270                                         vd->dataset[i] = (heat[i] + 2.0f) / 4.0f;
271                                 }
272                         }
273                         else if (vd->smoked_type == TEX_VD_SMOKEVEL) {
274                                 size_t totRes;
275                                 size_t i;
276                                 float *xvel, *yvel, *zvel;
277
278                                 copy_v3_v3_int(vd->resol, sds->res);
279                                 totRes = vd_resol_size(vd);
280                                 vd->dataset = MEM_mapallocN(sizeof(float) * (totRes), "smoke data");
281                                 /* get velocity data */
282                                 xvel = smoke_get_velocity_x(sds->fluid);
283                                 yvel = smoke_get_velocity_y(sds->fluid);
284                                 zvel = smoke_get_velocity_z(sds->fluid);
285
286                                 /* map velocities between 0 and 0.3f */
287                                 for (i = 0; i < totRes; i++) {
288                                         vd->dataset[i] = sqrtf(xvel[i] * xvel[i] + yvel[i] * yvel[i] + zvel[i] * zvel[i]) * 3.0f;
289                                 }
290
291                         }
292                         else if (vd->smoked_type == TEX_VD_SMOKEFLAME) {
293                                 size_t totRes;
294                                 float *flame;
295
296                                 if (sds->flags & MOD_SMOKE_HIGHRES) {
297                                         if (!smoke_turbulence_has_fuel(sds->wt)) {
298                                                 BLI_rw_mutex_unlock(sds->fluid_mutex);
299                                                 return;
300                                         }
301                                         smoke_turbulence_get_res(sds->wt, vd->resol);
302                                         flame = smoke_turbulence_get_flame(sds->wt);
303                                 }
304                                 else {
305                                         if (!smoke_has_fuel(sds->fluid)) {
306                                                 BLI_rw_mutex_unlock(sds->fluid_mutex);
307                                                 return;
308                                         }
309                                         copy_v3_v3_int(vd->resol, sds->res);
310                                         flame = smoke_get_flame(sds->fluid);
311                                 }
312
313                                 /* always store copy, as smoke internal data can change */
314                                 totRes = vd_resol_size(vd);
315                                 vd->dataset = MEM_mapallocN(sizeof(float)*(totRes), "smoke data");
316                                 memcpy(vd->dataset, flame, sizeof(float)*totRes);
317                         }
318                         else {
319                                 size_t totCells;
320                                 int depth = 4;
321                                 vd->data_type = TEX_VD_RGBA_PREMUL;
322
323                                 /* data resolution */
324                                 if (sds->flags & MOD_SMOKE_HIGHRES) {
325                                         smoke_turbulence_get_res(sds->wt, vd->resol);
326                                 }
327                                 else {
328                                         copy_v3_v3_int(vd->resol, sds->res);
329                                 }
330
331                                 /* TODO: is_vd_res_ok(rvd) doesnt check this resolution */
332                                 totCells = vd_resol_size(vd) * depth;
333                                 /* always store copy, as smoke internal data can change */
334                                 vd->dataset = MEM_mapallocN(sizeof(float) * totCells, "smoke data");
335
336                                 if (sds->flags & MOD_SMOKE_HIGHRES) {
337                                         if (smoke_turbulence_has_colors(sds->wt)) {
338                                                 smoke_turbulence_get_rgba(sds->wt, vd->dataset, 1);
339                                         }
340                                         else {
341                                                 smoke_turbulence_get_rgba_from_density(sds->wt, sds->active_color, vd->dataset, 1);
342                                         }
343                                 }
344                                 else {
345                                         if (smoke_has_colors(sds->fluid)) {
346                                                 smoke_get_rgba(sds->fluid, vd->dataset, 1);
347                                         }
348                                         else {
349                                                 smoke_get_rgba_from_density(sds->fluid, sds->active_color, vd->dataset, 1);
350                                         }
351                                 }
352                         }  /* end of fluid condition */
353
354                         BLI_rw_mutex_unlock(sds->fluid_mutex);
355                 }
356         }
357         
358         vd->ok = 1;
359
360 #else // WITH_SMOKE
361         (void)vd;
362         (void)cfra;
363
364         vd->dataset = NULL;
365 #endif
366 }
367
368 void cache_voxeldata(Tex *tex, int scene_frame)
369 {       
370         VoxelData *vd = tex->vd;
371         FILE *fp;
372         int curframe;
373         char path[sizeof(vd->source_path)];
374         
375         /* only re-cache if dataset needs updating */
376         if ((vd->flag & TEX_VD_STILL) || (vd->cachedframe == scene_frame))
377                 if (vd->ok) return;
378         
379         /* clear out old cache, ready for new */
380         if (vd->dataset) {
381                 MEM_freeN(vd->dataset);
382                 vd->dataset = NULL;
383         }
384         /* reset data_type */
385         vd->data_type = TEX_VD_INTENSITY;
386
387         if (vd->flag & TEX_VD_STILL)
388                 curframe = vd->still_frame;
389         else
390                 curframe = scene_frame;
391         
392         BLI_strncpy(path, vd->source_path, sizeof(path));
393         
394         switch (vd->file_format) {
395                 case TEX_VD_IMAGE_SEQUENCE:
396                         load_frame_image_sequence(vd, tex);
397                         return;
398                 case TEX_VD_SMOKE:
399                         init_frame_smoke(vd, scene_frame);
400                         return;
401                 case TEX_VD_BLENDERVOXEL:
402                         BLI_path_abs(path, G.main->name);
403                         if (!BLI_exists(path)) return;
404                         fp = BLI_fopen(path, "rb");
405                         if (!fp) return;
406                         
407                         if (read_voxeldata_header(fp, vd))
408                                 load_frame_blendervoxel(vd, fp, curframe - 1);
409
410                         fclose(fp);
411                         return;
412                 case TEX_VD_RAW_8BIT:
413                         BLI_path_abs(path, G.main->name);
414                         if (!BLI_exists(path)) return;
415                         fp = BLI_fopen(path, "rb");
416                         if (!fp) return;
417                         
418                         load_frame_raw8(vd, fp, curframe);
419                         fclose(fp);
420                         return;
421         }
422 }
423
424 void make_voxeldata(struct Render *re)
425 {
426         Tex *tex;
427         
428         re->i.infostr = IFACE_("Loading voxel datasets");
429         re->stats_draw(re->sdh, &re->i);
430         
431         /* XXX: should be doing only textures used in this render */
432         for (tex = re->main->tex.first; tex; tex = tex->id.next) {
433                 if (tex->id.us && tex->type == TEX_VOXELDATA) {
434                         cache_voxeldata(tex, re->r.cfra);
435                 }
436         }
437         
438         re->i.infostr = NULL;
439         re->stats_draw(re->sdh, &re->i);
440         
441 }
442
443 int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texres)
444 {        
445         VoxelData *vd = tex->vd;
446         float co[3], offset[3] = {0.5, 0.5, 0.5}, a;
447         int retval = (vd->data_type == TEX_VD_RGBA_PREMUL) ? TEX_RGB : TEX_INT;
448         int depth = (vd->data_type == TEX_VD_RGBA_PREMUL) ? 4 : 1;
449         int ch;
450
451         if (vd->dataset == NULL) {
452                 texres->tin = 0.0f;
453                 return 0;
454         }
455         
456         /* scale lookup from 0.0-1.0 (original location) to -1.0, 1.0, consistent with image texture tex coords */
457         /* in implementation this works backwards, bringing sample locations from -1.0, 1.0
458          * to the range 0.0, 1.0, before looking up in the voxel structure. */
459         copy_v3_v3(co, texvec);
460         mul_v3_fl(co, 0.5f);
461         add_v3_v3(co, offset);
462
463         /* co is now in the range 0.0, 1.0 */
464         switch (vd->extend) {
465                 case TEX_CLIP:
466                 {
467                         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)) {
468                                 texres->tin = 0.f;
469                                 return retval;
470                         }
471                         break;
472                 }
473                 case TEX_REPEAT:
474                 {
475                         co[0] = co[0] - floorf(co[0]);
476                         co[1] = co[1] - floorf(co[1]);
477                         co[2] = co[2] - floorf(co[2]);
478                         break;
479                 }
480                 case TEX_EXTEND:
481                 {
482                         CLAMP(co[0], 0.f, 1.f);
483                         CLAMP(co[1], 0.f, 1.f);
484                         CLAMP(co[2], 0.f, 1.f);
485                         break;
486                 }
487         }
488
489         for (ch = 0; ch < depth; ch++) {
490                 float *dataset = vd->dataset + ch*vd->resol[0]*vd->resol[1]*vd->resol[2];
491                 float *result = &texres->tin;
492
493                 if (vd->data_type == TEX_VD_RGBA_PREMUL) {
494                         switch (ch) {
495                                 case 0:
496                                         result = &texres->tr;
497                                         break;
498                                 case 1:
499                                         result = &texres->tg;
500                                         break;
501                                 case 2:
502                                         result = &texres->tb;
503                                         break;
504                         }
505                 }
506
507                 switch (vd->interp_type) {
508                         case TEX_VD_NEARESTNEIGHBOR:
509                                 *result = BLI_voxel_sample_nearest(dataset, vd->resol, co);
510                                 break;  
511                         case TEX_VD_LINEAR:
512                                 *result = BLI_voxel_sample_trilinear(dataset, vd->resol, co);
513                                 break;
514                         case TEX_VD_QUADRATIC:
515                                 *result = BLI_voxel_sample_triquadratic(dataset, vd->resol, co);
516                                 break;
517                         case TEX_VD_TRICUBIC_CATROM:
518                         case TEX_VD_TRICUBIC_BSPLINE:
519                                 *result = BLI_voxel_sample_tricubic(dataset, vd->resol, co, (vd->interp_type == TEX_VD_TRICUBIC_BSPLINE));
520                                 break;
521                 }
522         }
523
524         a = texres->tin;
525         texres->tin *= vd->int_multiplier;
526         BRICONT;
527         
528         if (vd->data_type == TEX_VD_RGBA_PREMUL) {
529                 /* unmultiply */
530                 if (a>0.001f) {
531                         texres->tr /= a;
532                         texres->tg /= a;
533                         texres->tb /= a;
534                 }
535                 texres->talpha = 1;
536         }
537         else {
538                 texres->tr = texres->tin;
539                 texres->tg = texres->tin;
540                 texres->tb = texres->tin;
541         }
542
543         texres->ta = texres->tin;
544         BRICONTRGB;
545         
546         return retval;
547 }