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