2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Raul Fernandez Hernandez (Farsthary), Matt Ebb.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/render/intern/source/voxeldata.c
37 #include "MEM_guardedalloc.h"
40 #include "BLI_blenlib.h"
41 #include "BLI_voxel.h"
42 #include "BLI_utildefines.h"
44 #include "IMB_imbuf.h"
45 #include "IMB_imbuf_types.h"
47 #include "BKE_global.h"
48 #include "BKE_image.h"
50 #include "BKE_modifier.h"
52 #include "smoke_API.h"
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"
61 #include "render_types.h"
62 #include "renderdatabase.h"
64 #include "voxeldata.h"
66 static int is_vd_res_ok(VoxelData *vd)
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);
75 /* use size_t because the result may exceed INT_MAX */
76 static size_t vd_resol_size(VoxelData *vd)
78 return (size_t)vd->resol[0] * (size_t)vd->resol[1] * (size_t)vd->resol[2];
81 static int load_frame_blendervoxel(VoxelData *vd, FILE *fp, int frame)
83 const size_t size = vd_resol_size(vd);
84 size_t offset = sizeof(VoxelDataHeader);
86 if (is_vd_res_ok(vd) == FALSE)
89 vd->dataset = MEM_mapallocN(sizeof(float)*size, "voxel dataset");
90 if (vd->dataset == NULL) return 0;
92 if (fseek(fp, frame*size*sizeof(float)+offset, 0) == -1)
94 if (fread(vd->dataset, sizeof(float), size, fp) != size)
97 vd->cachedframe = frame;
102 static int load_frame_raw8(VoxelData *vd, FILE *fp, int frame)
104 const size_t size = vd_resol_size(vd);
108 if (is_vd_res_ok(vd) == FALSE)
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);
120 if (fseek(fp,(frame-1)*size*sizeof(char),0) == -1) {
122 MEM_freeN(vd->dataset);
126 if (fread(data_c, sizeof(char), size, fp) != size) {
128 MEM_freeN(vd->dataset);
133 for (i=0; i<size; i++) {
134 vd->dataset[i] = (float)data_c[i] / 255.f;
138 vd->cachedframe = frame;
143 static void load_frame_image_sequence(VoxelData *vd, Tex *tex)
146 Image *ima = tex->ima;
147 ImageUser *tiuser = &tex->iuser;
148 ImageUser iuser = *(tiuser);
152 if (!ima || !tiuser) return;
153 if (iuser.frames == 0) return;
155 ima->source = IMA_SRC_SEQUENCE;
156 iuser.framenr = 1 + iuser.offset;
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)) {
163 ibuf= BKE_image_get_ibuf(ima, &iuser);
166 if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
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");
174 for (z=0; z < iuser.frames; z++) {
175 /* get a new ibuf for each frame */
178 ibuf= BKE_image_get_ibuf(ima, &iuser);
180 if (!ibuf->rect_float) IMB_float_from_rect(ibuf);
182 rf = ibuf->rect_float;
184 for (y=0; y < ibuf->y; y++) {
185 for (x=0; x < ibuf->x; x++) {
186 /* currently averaged to monchrome */
187 vd->dataset[ V_I(x, y, z, vd->resol) ] = (rf[0] + rf[1] + rf[2])*0.333f;
192 BKE_image_free_anim_ibufs(ima, iuser.framenr);
199 static int read_voxeldata_header(FILE *fp, struct VoxelData *vd)
201 VoxelDataHeader *h=(VoxelDataHeader *)MEM_mallocN(sizeof(VoxelDataHeader), "voxel data header");
204 if (fread(h,sizeof(VoxelDataHeader),1,fp) != 1) {
209 vd->resol[0]=h->resolX;
210 vd->resol[1]=h->resolY;
211 vd->resol[2]=h->resolZ;
217 static void init_frame_smoke(VoxelData *vd, float cfra)
224 if (vd->object == NULL) return;
227 /* draw code for smoke */
228 if ((md = (ModifierData *)modifiers_findByType(ob, eModifierType_Smoke))) {
229 SmokeModifierData *smd = (SmokeModifierData *)md;
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) {
240 copy_v3_v3_int(vd->resol, smd->domain->res);
241 totRes= vd_resol_size(vd);
243 // scaling heat values from -2.0-2.0 to 0.0-1.0
244 vd->dataset = MEM_mapallocN(sizeof(float)*(totRes), "smoke data");
247 heat = smoke_get_heat(smd->domain->fluid);
249 for (i=0; i<totRes; i++) {
250 vd->dataset[i] = (heat[i]+2.0f)/4.0f;
253 //vd->dataset = smoke_get_heat(smd->domain->fluid);
255 else if (vd->smoked_type == TEX_VD_SMOKEVEL) {
258 float *xvel, *yvel, *zvel;
260 copy_v3_v3_int(vd->resol, smd->domain->res);
261 totRes= vd_resol_size(vd);
263 // scaling heat values from -2.0-2.0 to 0.0-1.0
264 vd->dataset = MEM_mapallocN(sizeof(float)*(totRes), "smoke data");
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);
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;
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);
284 copy_v3_v3_int(vd->resol, smd->domain->res);
285 density = smoke_get_density(smd->domain->fluid);
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
307 void cache_voxeldata(Tex *tex, int scene_frame)
309 VoxelData *vd = tex->vd;
312 char path[sizeof(vd->source_path)];
314 /* only re-cache if dataset needs updating */
315 if ((vd->flag & TEX_VD_STILL) || (vd->cachedframe == scene_frame))
318 /* clear out old cache, ready for new */
320 MEM_freeN(vd->dataset);
324 if (vd->flag & TEX_VD_STILL)
325 curframe = vd->still_frame;
327 curframe = scene_frame;
329 BLI_strncpy(path, vd->source_path, sizeof(path));
331 switch (vd->file_format) {
332 case TEX_VD_IMAGE_SEQUENCE:
333 load_frame_image_sequence(vd, tex);
336 init_frame_smoke(vd, scene_frame);
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");
344 if (read_voxeldata_header(fp, vd))
345 load_frame_blendervoxel(vd, fp, curframe-1);
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");
355 load_frame_raw8(vd, fp, curframe);
361 void make_voxeldata(struct Render *re)
365 re->i.infostr= "Loading voxel datasets";
366 re->stats_draw(re->sdh, &re->i);
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);
376 re->stats_draw(re->sdh, &re->i);
380 int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texres)
382 int retval = TEX_INT;
383 VoxelData *vd = tex->vd;
384 float co[3], offset[3] = {0.5, 0.5, 0.5};
386 if (vd->dataset==NULL) {
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);
396 add_v3_v3(co, offset);
398 /* co is now in the range 0.0, 1.0 */
399 switch (vd->extend) {
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)) {
410 co[0] = co[0] - floorf(co[0]);
411 co[1] = co[1] - floorf(co[1]);
412 co[2] = co[2] - floorf(co[2]);
417 CLAMP(co[0], 0.f, 1.f);
418 CLAMP(co[1], 0.f, 1.f);
419 CLAMP(co[2], 0.f, 1.f);
424 switch (vd->interp_type) {
425 case TEX_VD_NEARESTNEIGHBOR:
426 texres->tin = voxel_sample_nearest(vd->dataset, vd->resol, co);
429 texres->tin = voxel_sample_trilinear(vd->dataset, vd->resol, co);
431 case TEX_VD_QUADRATIC:
432 texres->tin = voxel_sample_triquadratic(vd->dataset, vd->resol, co);
434 case TEX_VD_TRICUBIC_CATROM:
435 case TEX_VD_TRICUBIC_BSPLINE:
436 texres->tin = voxel_sample_tricubic(vd->dataset, vd->resol, co, (vd->interp_type == TEX_VD_TRICUBIC_BSPLINE));
440 texres->tin *= vd->int_multiplier;
443 texres->tr = texres->tin;
444 texres->tg = texres->tin;
445 texres->tb = texres->tin;
446 texres->ta = texres->tin;