diff --git a/release/ui/buttons_material.py b/release/ui/buttons_material.py index eaba26c7e51..21201166eda 100644 --- a/release/ui/buttons_material.py +++ b/release/ui/buttons_material.py @@ -73,6 +73,9 @@ class MATERIAL_PT_shading(MaterialButtonsPanel): __label__ = "Shading" COMPAT_ENGINES = set(['BLENDER_RENDER', 'BLENDER_GAME']) + def poll(self, context): + return (context.material.type in ('SURFACE', 'WIRE', 'HALO')) + def draw(self, context): layout = self.layout @@ -83,7 +86,7 @@ class MATERIAL_PT_shading(MaterialButtonsPanel): if mat: - if mat.type in ('SURFACE', 'WIRE', 'VOLUME'): + if mat.type in ('SURFACE', 'WIRE'): split = layout.split() col = split.column() @@ -113,6 +116,9 @@ class MATERIAL_PT_strand(MaterialButtonsPanel): mat = context.material return mat and (mat.type in ('SURFACE', 'WIRE')) and (context.scene.render_data.engine in self.COMPAT_ENGINES) + def poll(self, context): + return context.material.type in ('SURFACE', 'WIRE', 'HALO') + def draw(self, context): layout = self.layout @@ -170,6 +176,9 @@ class MATERIAL_PT_options(MaterialButtonsPanel): __label__ = "Options" COMPAT_ENGINES = set(['BLENDER_RENDER', 'BLENDER_GAME']) + def poll(self, context): + return (context.material.type in ('SURFACE', 'WIRE', 'HALO')) + def draw(self, context): layout = self.layout @@ -204,6 +213,9 @@ class MATERIAL_PT_shadow(MaterialButtonsPanel): __label__ = "Shadow" __default_closed__ = True COMPAT_ENGINES = set(['BLENDER_RENDER', 'BLENDER_GAME']) + + def poll(self, context): + return context.material.type in ('SURFACE', 'WIRE') def draw(self, context): layout = self.layout @@ -236,7 +248,7 @@ class MATERIAL_PT_diffuse(MaterialButtonsPanel): def poll(self, context): mat = context.material - return mat and (mat.type != 'HALO') and (context.scene.render_data.engine in self.COMPAT_ENGINES) + return mat and (mat.type in ('SURFACE', 'WIRE')) and (context.scene.render_data.engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -290,7 +302,7 @@ class MATERIAL_PT_specular(MaterialButtonsPanel): def poll(self, context): mat = context.material - return mat and (mat.type != 'HALO') and (context.scene.render_data.engine in self.COMPAT_ENGINES) + return mat and (mat.type in ('SURFACE', 'WIRE')) and (context.scene.render_data.engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -499,6 +511,125 @@ class MATERIAL_PT_transp(MaterialButtonsPanel): sub.active = rayt.gloss < 1 sub.itemR(rayt, "gloss_threshold", text="Threshold") sub.itemR(rayt, "gloss_samples", text="Samples") + +class MATERIAL_PT_volume_shading(MaterialButtonsPanel): + __label__ = "Shading" + __default_closed__ = False + COMPAT_ENGINES = set(['BLENDER_RENDER']) + + def poll(self, context): + return (context.material.type == 'VOLUME') and (context.scene.render_data.engine in self.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + mat = context.material + vol = context.material.volume + + split = layout.split() + + row = split.row() + row.itemR(vol, "density") + row.itemR(vol, "scattering") + + split = layout.split() + col = split.column() + col.itemR(vol, "absorption") + col.itemR(vol, "absorption_color", text="") + + + col = split.column() + col.itemR(vol, "emission") + col.itemR(vol, "emission_color", text="") + + + +class MATERIAL_PT_volume_scattering(MaterialButtonsPanel): + __label__ = "Scattering" + __default_closed__ = False + COMPAT_ENGINES = set(['BLENDER_RENDER']) + + def poll(self, context): + return (context.material.type == 'VOLUME') and (context.scene.render_data.engine in self.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + mat = context.material + vol = context.material.volume + + split = layout.split() + + col = split.column() + col.itemR(vol, "scattering_mode", text="") + if vol.scattering_mode == 'SINGLE_SCATTERING': + col.itemR(vol, "light_cache") + sub = col.column() + sub.active = vol.light_cache + sub.itemR(vol, "cache_resolution") + elif vol.scattering_mode in ('MULTIPLE_SCATTERING', 'SINGLE_PLUS_MULTIPLE_SCATTERING'): + col.itemR(vol, "cache_resolution") + + col = col.column(align=True) + col.itemR(vol, "ms_diffusion") + col.itemR(vol, "ms_spread") + col.itemR(vol, "ms_intensity") + + col = split.column() + # col.itemL(text="Anisotropic Scattering:") + col.itemR(vol, "phase_function", text="") + if vol.phase_function in ('SCHLICK', 'HENYEY-GREENSTEIN'): + col.itemR(vol, "asymmetry") + +class MATERIAL_PT_volume_transp(MaterialButtonsPanel): + __label__= "Transparency" + COMPAT_ENGINES = set(['BLENDER_RENDER']) + + def poll(self, context): + mat = context.material + return mat and (mat.type == 'VOLUME') and (context.scene.render_data.engine in self.COMPAT_ENGINES) + + def draw_header(self, context): + layout = self.layout + + def draw(self, context): + layout = self.layout + + mat = context.material + rayt = context.material.raytrace_transparency + + row= layout.row() + row.itemR(mat, "transparency_method", expand=True) + row.active = mat.transparency and (not mat.shadeless) + +class MATERIAL_PT_volume_integration(MaterialButtonsPanel): + __label__ = "Integration" + __default_closed__ = False + COMPAT_ENGINES = set(['BLENDER_RENDER']) + + def poll(self, context): + return (context.material.type == 'VOLUME') and (context.scene.render_data.engine in self.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + mat = context.material + vol = context.material.volume + + split = layout.split() + + col = split.column() + col.itemL(text="Step Calculation:") + col.itemR(vol, "step_calculation", text="") + col = col.column(align=True) + col.itemR(vol, "step_size") + col.itemR(vol, "shading_step_size") + + col = split.column() + col.itemL() + col.itemR(vol, "depth_cutoff") + col.itemR(vol, "density_scale") + class MATERIAL_PT_halo(MaterialButtonsPanel): __label__= "Halo" @@ -587,6 +718,10 @@ bpy.types.register(MATERIAL_PT_shading) bpy.types.register(MATERIAL_PT_transp) bpy.types.register(MATERIAL_PT_mirror) bpy.types.register(MATERIAL_PT_sss) +bpy.types.register(MATERIAL_PT_volume_shading) +bpy.types.register(MATERIAL_PT_volume_scattering) +bpy.types.register(MATERIAL_PT_volume_transp) +bpy.types.register(MATERIAL_PT_volume_integration) bpy.types.register(MATERIAL_PT_halo) bpy.types.register(MATERIAL_PT_flare) bpy.types.register(MATERIAL_PT_physics) diff --git a/release/ui/buttons_texture.py b/release/ui/buttons_texture.py index 20b3c7bca3e..4827f677336 100644 --- a/release/ui/buttons_texture.py +++ b/release/ui/buttons_texture.py @@ -210,35 +210,51 @@ class TEXTURE_PT_influence(TextureSlotPanel): sub.itemR(tex, factor, text=name, slider=True) if ma: - split = layout.split() - - col = split.column() - col.itemL(text="Diffuse:") - factor_but(col, tex.map_diffuse, "map_diffuse", "diffuse_factor", "Intensity") - factor_but(col, tex.map_colordiff, "map_colordiff", "colordiff_factor", "Color") - factor_but(col, tex.map_alpha, "map_alpha", "alpha_factor", "Alpha") - factor_but(col, tex.map_translucency, "map_translucency", "translucency_factor", "Translucency") + if ma.type in ['SURFACE', 'HALO', 'WIRE']: + split = layout.split() + + col = split.column() + col.itemL(text="Diffuse:") + factor_but(col, tex.map_diffuse, "map_diffuse", "diffuse_factor", "Intensity") + factor_but(col, tex.map_colordiff, "map_colordiff", "colordiff_factor", "Color") + factor_but(col, tex.map_alpha, "map_alpha", "alpha_factor", "Alpha") + factor_but(col, tex.map_translucency, "map_translucency", "translucency_factor", "Translucency") - col.itemL(text="Specular:") - factor_but(col, tex.map_specular, "map_specular", "specular_factor", "Intensity") - factor_but(col, tex.map_colorspec, "map_colorspec", "colorspec_factor", "Color") - factor_but(col, tex.map_hardness, "map_hardness", "hardness_factor", "Hardness") + col.itemL(text="Specular:") + factor_but(col, tex.map_specular, "map_specular", "specular_factor", "Intensity") + factor_but(col, tex.map_colorspec, "map_colorspec", "colorspec_factor", "Color") + factor_but(col, tex.map_hardness, "map_hardness", "hardness_factor", "Hardness") - col = split.column() - col.itemL(text="Shading:") - factor_but(col, tex.map_ambient, "map_ambient", "ambient_factor", "Ambient") - factor_but(col, tex.map_emit, "map_emit", "emit_factor", "Emit") - factor_but(col, tex.map_mirror, "map_mirror", "mirror_factor", "Mirror") - factor_but(col, tex.map_raymir, "map_raymir", "raymir_factor", "Ray Mirror") + col = split.column() + col.itemL(text="Shading:") + factor_but(col, tex.map_ambient, "map_ambient", "ambient_factor", "Ambient") + factor_but(col, tex.map_emit, "map_emit", "emit_factor", "Emit") + factor_but(col, tex.map_mirror, "map_mirror", "mirror_factor", "Mirror") + factor_but(col, tex.map_raymir, "map_raymir", "raymir_factor", "Ray Mirror") - col.itemL(text="Geometry:") - factor_but(col, tex.map_normal, "map_normal", "normal_factor", "Normal") - factor_but(col, tex.map_warp, "map_warp", "warp_factor", "Warp") - factor_but(col, tex.map_displacement, "map_displacement", "displacement_factor", "Displace") + col.itemL(text="Geometry:") + factor_but(col, tex.map_normal, "map_normal", "normal_factor", "Normal") + factor_but(col, tex.map_warp, "map_warp", "warp_factor", "Warp") + factor_but(col, tex.map_displacement, "map_displacement", "displacement_factor", "Displace") + + #sub = col.column() + #sub.active = tex.map_translucency or tex.map_emit or tex.map_alpha or tex.map_raymir or tex.map_hardness or tex.map_ambient or tex.map_specularity or tex.map_reflection or tex.map_mirror + #sub.itemR(tex, "default_value", text="Amount", slider=True) + elif ma.type == 'VOLUME': + split = layout.split() + + col = split.column() + factor_but(col, tex.map_density, "map_density", "density_factor", "Density") + factor_but(col, tex.map_emission, "map_emission", "emission_factor", "Emission") + factor_but(col, tex.map_absorption, "map_absorption", "absorption_factor", "Absorption") + factor_but(col, tex.map_scattering, "map_scattering", "scattering_factor", "Scattering") + + col = split.column() + col.itemL(text=" ") + factor_but(col, tex.map_alpha, "map_coloremission", "coloremission_factor", "Emission Color") + factor_but(col, tex.map_colorabsorption, "map_colorabsorption", "colorabsorption_factor", "Absorption Color") + - #sub = col.column() - #sub.active = tex.map_translucency or tex.map_emit or tex.map_alpha or tex.map_raymir or tex.map_hardness or tex.map_ambient or tex.map_specularity or tex.map_reflection or tex.map_mirror - #sub.itemR(tex, "default_value", text="Amount", slider=True) elif la: row = layout.row() factor_but(row, tex.map_color, "map_color", "color_factor", "Color") @@ -596,6 +612,62 @@ class TEXTURE_PT_distortednoise(TextureTypePanel): flow.itemR(tex, "distortion_amount", text="Distortion") flow.itemR(tex, "noise_size", text="Size") flow.itemR(tex, "nabla") + +class TEXTURE_PT_voxeldata(TextureButtonsPanel): + __idname__= "TEXTURE_PT_voxeldata" + __label__ = "Voxel Data" + + def poll(self, context): + tex = context.texture + return (tex and tex.type == 'VOXEL_DATA') + + def draw(self, context): + layout = self.layout + tex = context.texture + vd = tex.voxeldata + + layout.itemR(vd, "file_format") + if vd.file_format in ['BLENDER_VOXEL', 'RAW_8BIT']: + layout.itemR(vd, "source_path") + if vd.file_format == 'RAW_8BIT': + layout.itemR(vd, "resolution") + if vd.file_format == 'SMOKE': + layout.itemR(vd, "domain_object") + + layout.itemR(vd, "still") + if vd.still: + layout.itemR(vd, "still_frame_number") + + layout.itemR(vd, "interpolation") + layout.itemR(vd, "intensity") + +class TEXTURE_PT_pointdensity(TextureButtonsPanel): + __idname__= "TEXTURE_PT_pointdensity" + __label__ = "Point Density" + + def poll(self, context): + tex = context.texture + return (tex and tex.type == 'POINT_DENSITY') + + def draw(self, context): + layout = self.layout + tex = context.texture + pd = tex.pointdensity + + layout.itemR(pd, "point_source") + layout.itemR(pd, "object") + if pd.point_source == 'PARTICLE_SYSTEM': + layout.item_pointerR(pd, "particle_system", pd.object, "particle_systems", text="") + layout.itemR(pd, "radius") + layout.itemR(pd, "falloff") + if pd.falloff == 'SOFT': + layout.itemR(pd, "falloff_softness") + layout.itemR(pd, "color_source") + layout.itemR(pd, "turbulence") + layout.itemR(pd, "turbulence_size") + layout.itemR(pd, "turbulence_depth") + layout.itemR(pd, "turbulence_influence") + bpy.types.register(TEXTURE_PT_context_texture) bpy.types.register(TEXTURE_PT_preview) @@ -613,6 +685,8 @@ bpy.types.register(TEXTURE_PT_envmap) bpy.types.register(TEXTURE_PT_musgrave) bpy.types.register(TEXTURE_PT_voronoi) bpy.types.register(TEXTURE_PT_distortednoise) +bpy.types.register(TEXTURE_PT_voxeldata) +bpy.types.register(TEXTURE_PT_pointdensity) bpy.types.register(TEXTURE_PT_colors) bpy.types.register(TEXTURE_PT_mapping) bpy.types.register(TEXTURE_PT_influence) diff --git a/source/blender/blenkernel/BKE_texture.h b/source/blender/blenkernel/BKE_texture.h index a1600ce5473..a9862ba586b 100644 --- a/source/blender/blenkernel/BKE_texture.h +++ b/source/blender/blenkernel/BKE_texture.h @@ -40,6 +40,8 @@ struct ColorBand; struct HaloRen; struct TexMapping; struct EnvMap; +struct PointDensity; +struct VoxelData; /* in ColorBand struct */ #define MAXCOLORBAND 32 @@ -75,6 +77,16 @@ void BKE_free_envmap(struct EnvMap *env); struct EnvMap *BKE_add_envmap(void); struct EnvMap *BKE_copy_envmap(struct EnvMap *env); +void BKE_free_pointdensitydata(struct PointDensity *pd); +void BKE_free_pointdensity(struct PointDensity *pd); +struct PointDensity *BKE_add_pointdensity(void); +struct PointDensity *BKE_copy_pointdensity(struct PointDensity *pd); + +void BKE_free_voxeldatadata(struct VoxelData *vd); +void BKE_free_voxeldata(struct VoxelData *vd); +struct VoxelData *BKE_add_voxeldata(void); +struct VoxelData *BKE_copy_voxeldata(struct VoxelData *vd); + int BKE_texture_dependsOnTime(const struct Tex *texture); #endif diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 62ec12349aa..53de570b55e 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -170,6 +170,21 @@ void init_material(Material *ma) ma->sss_front= 1.0f; ma->sss_back= 1.0f; + ma->vol.density = 1.0f; + ma->vol.emission = 0.0f; + ma->vol.absorption = 1.0f; + ma->vol.scattering = 1.0f; + ma->vol.emission_col[0] = ma->vol.emission_col[1] = ma->vol.emission_col[2] = 1.0f; + ma->vol.absorption_col[0] = ma->vol.absorption_col[1] = ma->vol.absorption_col[2] = 0.0f; + ma->vol.density_scale = 1.0f; + ma->vol.depth_cutoff = 0.01f; + ma->vol.stepsize_type = MA_VOL_STEP_RANDOMIZED; + ma->vol.stepsize = 0.2f; + ma->vol.shade_stepsize = 0.2f; + ma->vol.shade_type = MA_VOL_SHADE_SINGLE; + ma->vol.shadeflag |= MA_VOL_PRECACHESHADING; + ma->vol.precache_resolution = 50; + ma->mode= MA_TRACEBLE|MA_SHADBUF|MA_SHADOW|MA_RAYBIAS|MA_TANGENT_STR|MA_ZTRANSP; ma->preview = NULL; diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index eeffbfe5ef6..96e9f54fc0c 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -43,6 +43,7 @@ #include "BLI_blenlib.h" #include "BLI_arithb.h" #include "BLI_rand.h" +#include "BLI_kdopbvh.h" #include "DNA_texture_types.h" #include "DNA_key_types.h" @@ -417,6 +418,8 @@ void free_texture(Tex *tex) free_plugin_tex(tex->plugin); if(tex->coba) MEM_freeN(tex->coba); if(tex->env) BKE_free_envmap(tex->env); + if(tex->pd) BKE_free_pointdensity(tex->pd); + if(tex->vd) BKE_free_voxeldata(tex->vd); BKE_previewimg_free(&tex->preview); BKE_icon_delete((struct ID*)tex); tex->id.icon_id = 0; @@ -486,6 +489,16 @@ void default_tex(Tex *tex) tex->env->depth=0; } + if (tex->pd) { + tex->pd->radius = 0.3f; + tex->pd->falloff_type = TEX_PD_FALLOFF_STD; + } + + if (tex->vd) { + tex->vd->resol[0] = tex->vd->resol[1] = tex->vd->resol[2] = 0; + tex->vd->interp_type=TEX_VD_LINEAR; + tex->vd->file_format=TEX_VD_SMOKE; + } pit = tex->plugin; if (pit) { varstr= pit->varstr; @@ -739,7 +752,7 @@ void autotexname(Tex *tex) { char texstr[20][12]= {"None" , "Clouds" , "Wood", "Marble", "Magic" , "Blend", "Stucci", "Noise" , "Image", "Plugin", "EnvMap" , "Musgrave", - "Voronoi", "DistNoise", "", "", "", "", "", ""}; + "Voronoi", "DistNoise", "Point Density", "Voxel Data", "", "", "", ""}; Image *ima; char di[FILE_MAXDIR], fi[FILE_MAXFILE]; @@ -887,6 +900,106 @@ void BKE_free_envmap(EnvMap *env) } +/* ------------------------------------------------------------------------- */ + +PointDensity *BKE_add_pointdensity(void) +{ + PointDensity *pd; + + pd= MEM_callocN(sizeof(PointDensity), "pointdensity"); + pd->flag = 0; + pd->radius = 0.3f; + pd->falloff_type = TEX_PD_FALLOFF_STD; + pd->falloff_softness = 2.0; + pd->source = TEX_PD_PSYS; + pd->point_tree = NULL; + pd->point_data = NULL; + pd->noise_size = 0.5f; + pd->noise_depth = 1; + pd->noise_fac = 1.0f; + pd->noise_influence = TEX_PD_NOISE_STATIC; + pd->coba = add_colorband(1); + pd->speed_scale = 1.0f; + pd->totpoints = 0; + pd->coba = add_colorband(1); + pd->object = NULL; + pd->psys = NULL; + return pd; +} + +PointDensity *BKE_copy_pointdensity(PointDensity *pd) +{ + PointDensity *pdn; + + pdn= MEM_dupallocN(pd); + pdn->point_tree = NULL; + pdn->point_data = NULL; + if(pdn->coba) pdn->coba= MEM_dupallocN(pdn->coba); + + return pdn; +} + +void BKE_free_pointdensitydata(PointDensity *pd) +{ + if (pd->point_tree) { + BLI_bvhtree_free(pd->point_tree); + pd->point_tree = NULL; + } + if (pd->point_data) { + MEM_freeN(pd->point_data); + pd->point_data = NULL; + } + if(pd->coba) MEM_freeN(pd->coba); +} + +void BKE_free_pointdensity(PointDensity *pd) +{ + BKE_free_pointdensitydata(pd); + MEM_freeN(pd); +} + + +void BKE_free_voxeldatadata(struct VoxelData *vd) +{ + if (vd->dataset) { + MEM_freeN(vd->dataset); + vd->dataset = NULL; + } + +} + +void BKE_free_voxeldata(struct VoxelData *vd) +{ + BKE_free_voxeldatadata(vd); + MEM_freeN(vd); +} + +struct VoxelData *BKE_add_voxeldata(void) +{ + VoxelData *vd; + + vd= MEM_callocN(sizeof(struct VoxelData), "voxeldata"); + vd->dataset = NULL; + vd->resol[0] = vd->resol[1] = vd->resol[2] = 1; + vd->interp_type= TEX_VD_LINEAR; + vd->file_format= TEX_VD_SMOKE; + vd->int_multiplier = 1.0; + vd->object = NULL; + + return vd; + } + +struct VoxelData *BKE_copy_voxeldata(struct VoxelData *vd) +{ + VoxelData *vdn; + + vdn= MEM_dupallocN(vd); + vdn->dataset = NULL; + + return vdn; +} + + /* ------------------------------------------------------------------------- */ int BKE_texture_dependsOnTime(const struct Tex *texture) { diff --git a/source/blender/blenlib/BLI_arithb.h b/source/blender/blenlib/BLI_arithb.h index 1502c4870be..0a0749d3382 100644 --- a/source/blender/blenlib/BLI_arithb.h +++ b/source/blender/blenlib/BLI_arithb.h @@ -325,6 +325,7 @@ void printvec4f(char *str, float v[4]); void VecAddf(float *v, float *v1, float *v2); void VecSubf(float *v, float *v1, float *v2); +void VecMulVecf(float *v, float *v1, float *v2); void VecLerpf(float *target, float *a, float *b, float t); void VecMidf(float *v, float *v1, float *v2); diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index 50462d531ef..fe6bc576fbd 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -71,6 +71,8 @@ typedef void (*BVHTree_NearestPointCallback) (void *userdata, int index, const f /* callback must update hit in case it finds a nearest successful hit */ typedef void (*BVHTree_RayCastCallback) (void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit); +/* callback to range search query */ +typedef void (*BVHTree_RangeQuery) (void *userdata, int index, float squared_dist); BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis); void BLI_bvhtree_free(BVHTree *tree); @@ -95,5 +97,9 @@ int BLI_bvhtree_ray_cast(BVHTree *tree, const float *co, const float *dir, float float BLI_bvhtree_bb_raycast(float *bv, float *light_start, float *light_end, float *pos); +/* range query */ +int BLI_bvhtree_range_query(BVHTree *tree, const float *co, float radius, BVHTree_RangeQuery callback, void *userdata); + + #endif // BLI_KDOPBVH_H diff --git a/source/blender/blenlib/BLI_voxel.h b/source/blender/blenlib/BLI_voxel.h new file mode 100644 index 00000000000..091d8e3682d --- /dev/null +++ b/source/blender/blenlib/BLI_voxel.h @@ -0,0 +1,40 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Matt Ebb, Raul Fernandez Hernandez (Farsthary). + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BLI_VOXEL_H +#define BLI_VOXEL_H + +/* find the index number of a voxel, given x/y/z integer coords and resolution vector */ +#define V_I(x, y, z, res) ( (z)*(res)[1]*(res)[0] + (y)*(res)[0] + (x) ) + +/* all input coordinates must be in bounding box 0.0 - 1.0 */ +float voxel_sample_nearest(float *data, int *res, float *co); +float voxel_sample_trilinear(float *data, int *res, float *co); +float voxel_sample_tricubic(float *data, int *res, float *co); + +#endif /* BLI_VOXEL_H */ \ No newline at end of file diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index 07e81b291f5..61d9cce1a58 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -72,10 +72,10 @@ struct BVHTree char start_axis, stop_axis; // KDOP_AXES array indices according to axis }; -typedef struct BVHOverlapData -{ - BVHTree *tree1, *tree2; - BVHTreeOverlap *overlap; +typedef struct BVHOverlapData +{ + BVHTree *tree1, *tree2; + BVHTreeOverlap *overlap; int i, max_overlap; /* i is number of overlaps */ int start_axis, stop_axis; } BVHOverlapData; @@ -109,7 +109,7 @@ typedef struct BVHRayCastData //////////////////////////////////////////////////////////////////////// // Bounding Volume Hierarchy Definition -// +// // Notes: From OBB until 26-DOP --> all bounding volumes possible, just choose type below // Notes: You have to choose the type at compile time ITM // Notes: You can choose the tree type --> binary, quad, octree, choose below @@ -188,10 +188,10 @@ int ADJUST_MEMORY(void *local_memblock, void **memblock, int new_size, int *max_ ////////////////////////////////////////////////////////////////////////////////////////////////////// -// Introsort +// Introsort // with permission deriven from the following Java code: // http://ralphunden.net/content/tutorials/a-guide-to-introsort/ -// and he derived it from the SUN STL +// and he derived it from the SUN STL ////////////////////////////////////////////////////////////////////////////////////////////////////// static int size_threshold = 16; /* @@ -362,7 +362,7 @@ static void create_kdop_hull(BVHTree *tree, BVHNode *node, float *co, int numpoi float newminmax; float *bv = node->bv; int i, k; - + // don't init boudings for the moving case if(!moving) { @@ -372,7 +372,7 @@ static void create_kdop_hull(BVHTree *tree, BVHNode *node, float *co, int numpoi bv[2*i + 1] = -FLT_MAX; } } - + for(k = 0; k < numpoints; k++) { // for all Axes. @@ -394,7 +394,7 @@ static void refit_kdop_hull(BVHTree *tree, BVHNode *node, int start, int end) int i, j; float *bv = node->bv; - + for (i = tree->start_axis; i < tree->stop_axis; i++) { bv[2*i] = FLT_MAX; @@ -406,10 +406,10 @@ static void refit_kdop_hull(BVHTree *tree, BVHNode *node, int start, int end) // for all Axes. for (i = tree->start_axis; i < tree->stop_axis; i++) { - newmin = tree->nodes[j]->bv[(2 * i)]; + newmin = tree->nodes[j]->bv[(2 * i)]; if ((newmin < bv[(2 * i)])) bv[(2 * i)] = newmin; - + newmax = tree->nodes[j]->bv[(2 * i) + 1]; if ((newmax > bv[(2 * i) + 1])) bv[(2 * i) + 1] = newmax; @@ -427,14 +427,14 @@ static char get_largest_axis(float *bv) middle_point[0] = (bv[1]) - (bv[0]); // x axis middle_point[1] = (bv[3]) - (bv[2]); // y axis middle_point[2] = (bv[5]) - (bv[4]); // z axis - if (middle_point[0] > middle_point[1]) + if (middle_point[0] > middle_point[1]) { if (middle_point[0] > middle_point[2]) return 1; // max x axis else return 5; // max z axis } - else + else { if (middle_point[1] > middle_point[2]) return 3; // max y axis @@ -448,24 +448,24 @@ static char get_largest_axis(float *bv) static void node_join(BVHTree *tree, BVHNode *node) { int i, j; - + for (i = tree->start_axis; i < tree->stop_axis; i++) { node->bv[2*i] = FLT_MAX; node->bv[2*i + 1] = -FLT_MAX; } - + for (i = 0; i < tree->tree_type; i++) { - if (node->children[i]) + if (node->children[i]) { for (j = tree->start_axis; j < tree->stop_axis; j++) { - // update minimum - if (node->children[i]->bv[(2 * j)] < node->bv[(2 * j)]) + // update minimum + if (node->children[i]->bv[(2 * j)] < node->bv[(2 * j)]) node->bv[(2 * j)] = node->children[i]->bv[(2 * j)]; - - // update maximum + + // update maximum if (node->children[i]->bv[(2 * j) + 1] > node->bv[(2 * j) + 1]) node->bv[(2 * j) + 1] = node->children[i]->bv[(2 * j) + 1]; } @@ -518,7 +518,7 @@ static void bvhtree_info(BVHTree *tree) static void verify_tree(BVHTree *tree) { int i, j, check = 0; - + // check the pointer list for(i = 0; i < tree->totleaf; i++) { @@ -538,7 +538,7 @@ static void verify_tree(BVHTree *tree) check = 0; } } - + // check the leaf list for(i = 0; i < tree->totleaf; i++) { @@ -558,7 +558,7 @@ static void verify_tree(BVHTree *tree) check = 0; } } - + printf("branches: %d, leafs: %d, total: %d\n", tree->totbranch, tree->totleaf, tree->totbranch + tree->totleaf); } #endif @@ -703,7 +703,7 @@ static void non_recursive_bvh_div_nodes(BVHTree *tree, BVHNode *branches_array, BVHBuildHelper data; int depth; - + // set parent from root node to NULL BVHNode *tmp = branches_array+0; tmp->parent = NULL; @@ -722,7 +722,7 @@ static void non_recursive_bvh_div_nodes(BVHTree *tree, BVHNode *branches_array, } branches_array--; //Implicit trees use 1-based indexs - + build_implicit_tree_helper(tree, &data); //Loop tree levels (log N) loops @@ -806,11 +806,11 @@ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) { BVHTree *tree; int numnodes, i; - + // theres not support for trees below binary-trees :P if(tree_type < 2) return NULL; - + if(tree_type > MAX_TREETYPE) return NULL; @@ -820,13 +820,13 @@ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) //so that tangent rays can still hit a bounding volume.. //this bug would show up when casting a ray aligned with a kdop-axis and with an edge of 2 faces epsilon = MAX2(FLT_EPSILON, epsilon); - + if(tree) { tree->epsilon = epsilon; - tree->tree_type = tree_type; + tree->tree_type = tree_type; tree->axis = axis; - + if(axis == 26) { tree->start_axis = 0; @@ -863,13 +863,13 @@ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) numnodes = maxsize + implicit_needed_branches(tree_type, maxsize) + tree_type; tree->nodes = (BVHNode **)MEM_callocN(sizeof(BVHNode *)*numnodes, "BVHNodes"); - + if(!tree->nodes) { MEM_freeN(tree); return NULL; } - + tree->nodebv = (float*)MEM_callocN(sizeof(float)* axis * numnodes, "BVHNodeBV"); if(!tree->nodebv) { @@ -886,7 +886,7 @@ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) } tree->nodearray = (BVHNode *)MEM_callocN(sizeof(BVHNode)* numnodes, "BVHNodeArray"); - + if(!tree->nodearray) { MEM_freeN(tree->nodechild); @@ -902,14 +902,14 @@ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) tree->nodearray[i].bv = tree->nodebv + i * axis; tree->nodearray[i].children = tree->nodechild + i * tree_type; } - + } return tree; } void BLI_bvhtree_free(BVHTree *tree) -{ +{ if(tree) { MEM_freeN(tree->nodes); @@ -946,27 +946,27 @@ int BLI_bvhtree_insert(BVHTree *tree, int index, float *co, int numpoints) { int i; BVHNode *node = NULL; - + // insert should only possible as long as tree->totbranch is 0 if(tree->totbranch > 0) return 0; - + if(tree->totleaf+1 >= MEM_allocN_len(tree->nodes)/sizeof(*(tree->nodes))) return 0; - + // TODO check if have enough nodes in array - + node = tree->nodes[tree->totleaf] = &(tree->nodearray[tree->totleaf]); tree->totleaf++; - + create_kdop_hull(tree, node, co, numpoints, 0); node->index= index; - + // inflate the bv with some epsilon for (i = tree->start_axis; i < tree->stop_axis; i++) { - node->bv[(2 * i)] -= tree->epsilon; // minimum - node->bv[(2 * i) + 1] += tree->epsilon; // maximum + node->bv[(2 * i)] -= tree->epsilon; // minimum + node->bv[(2 * i) + 1] += tree->epsilon; // maximum } return 1; @@ -978,23 +978,23 @@ int BLI_bvhtree_update_node(BVHTree *tree, int index, float *co, float *co_movin { int i; BVHNode *node= NULL; - + // check if index exists if(index > tree->totleaf) return 0; - + node = tree->nodearray + index; - + create_kdop_hull(tree, node, co, numpoints, 0); - + if(co_moving) create_kdop_hull(tree, node, co_moving, numpoints, 1); - + // inflate the bv with some epsilon for (i = tree->start_axis; i < tree->stop_axis; i++) { - node->bv[(2 * i)] -= tree->epsilon; // minimum - node->bv[(2 * i) + 1] += tree->epsilon; // maximum + node->bv[(2 * i)] -= tree->epsilon; // minimum + node->bv[(2 * i) + 1] += tree->epsilon; // maximum } return 1; @@ -1030,24 +1030,24 @@ static int tree_overlap(BVHNode *node1, BVHNode *node2, int start_axis, int stop float *bv2 = node2->bv; float *bv1_end = bv1 + (stop_axis<<1); - + bv1 += start_axis<<1; bv2 += start_axis<<1; - + // test all axis if min + max overlap for (; bv1 != bv1_end; bv1+=2, bv2+=2) { - if ((*(bv1) > *(bv2 + 1)) || (*(bv2) > *(bv1 + 1))) + if ((*(bv1) > *(bv2 + 1)) || (*(bv2) > *(bv1 + 1))) return 0; } - + return 1; } static void traverse(BVHOverlapData *data, BVHNode *node1, BVHNode *node2) { int j; - + if(tree_overlap(node1, node2, data->start_axis, data->stop_axis)) { // check if node1 is a leaf @@ -1056,17 +1056,17 @@ static void traverse(BVHOverlapData *data, BVHNode *node1, BVHNode *node2) // check if node2 is a leaf if(!node2->totnode) { - + if(node1 == node2) { return; } - + if(data->i >= data->max_overlap) - { + { // try to make alloc'ed memory bigger data->overlap = realloc(data->overlap, sizeof(BVHTreeOverlap)*data->max_overlap*2); - + if(!data->overlap) { printf("Out of Memory in traverse\n"); @@ -1074,7 +1074,7 @@ static void traverse(BVHOverlapData *data, BVHNode *node1, BVHNode *node2) } data->max_overlap *= 2; } - + // both leafs, insert overlap! data->overlap[data->i].indexA = node1->index; data->overlap[data->i].indexB = node2->index; @@ -1092,7 +1092,7 @@ static void traverse(BVHOverlapData *data, BVHNode *node1, BVHNode *node2) } else { - + for(j = 0; j < data->tree2->tree_type; j++) { if(node1->children[j]) @@ -1108,21 +1108,21 @@ BVHTreeOverlap *BLI_bvhtree_overlap(BVHTree *tree1, BVHTree *tree2, int *result) int j, total = 0; BVHTreeOverlap *overlap = NULL, *to = NULL; BVHOverlapData **data; - + // check for compatibility of both trees (can't compare 14-DOP with 18-DOP) if((tree1->axis != tree2->axis) && (tree1->axis == 14 || tree2->axis == 14) && (tree1->axis == 18 || tree2->axis == 18)) return 0; - + // fast check root nodes for collision before doing big splitting + traversal if(!tree_overlap(tree1->nodes[tree1->totleaf], tree2->nodes[tree2->totleaf], MIN2(tree1->start_axis, tree2->start_axis), MIN2(tree1->stop_axis, tree2->stop_axis))) return 0; data = MEM_callocN(sizeof(BVHOverlapData *)* tree1->tree_type, "BVHOverlapData_star"); - + for(j = 0; j < tree1->tree_type; j++) { data[j] = (BVHOverlapData *)MEM_callocN(sizeof(BVHOverlapData), "BVHOverlapData"); - + // init BVHOverlapData data[j]->overlap = (BVHTreeOverlap *)malloc(sizeof(BVHTreeOverlap)*MAX2(tree1->totleaf, tree2->totleaf)); data[j]->tree1 = tree1; @@ -1138,25 +1138,25 @@ BVHTreeOverlap *BLI_bvhtree_overlap(BVHTree *tree1, BVHTree *tree2, int *result) { traverse(data[j], tree1->nodes[tree1->totleaf]->children[j], tree2->nodes[tree2->totleaf]); } - + for(j = 0; j < tree1->tree_type; j++) total += data[j]->i; - + to = overlap = (BVHTreeOverlap *)MEM_callocN(sizeof(BVHTreeOverlap)*total, "BVHTreeOverlap"); - + for(j = 0; j < tree1->tree_type; j++) { memcpy(to, data[j]->overlap, data[j]->i*sizeof(BVHTreeOverlap)); to+=data[j]->i; } - + for(j = 0; j < tree1->tree_type; j++) { free(data[j]->overlap); MEM_freeN(data[j]); } MEM_freeN(data); - + (*result) = total; return overlap; } @@ -1173,7 +1173,7 @@ static float squared_dist(const float *a, const float *b) } //Determines the nearest point of the given node BV. Returns the squared distance to that point. -static float calc_nearest_point(BVHNearestData *data, BVHNode *node, float *nearest) +static float calc_nearest_point(const float *proj, BVHNode *node, float *nearest) { int i; const float *bv = node->bv; @@ -1181,12 +1181,12 @@ static float calc_nearest_point(BVHNearestData *data, BVHNode *node, float *near //nearest on AABB hull for(i=0; i != 3; i++, bv += 2) { - if(bv[0] > data->proj[i]) + if(bv[0] > proj[i]) nearest[i] = bv[0]; - else if(bv[1] < data->proj[i]) + else if(bv[1] < proj[i]) nearest[i] = bv[1]; else - nearest[i] = data->proj[i]; + nearest[i] = proj[i]; } /* @@ -1208,7 +1208,7 @@ static float calc_nearest_point(BVHNearestData *data, BVHNode *node, float *near } } */ - return squared_dist(data->co, nearest); + return squared_dist(proj, nearest); } @@ -1231,7 +1231,7 @@ static void dfs_find_nearest_dfs(BVHNearestData *data, BVHNode *node) else { data->nearest.index = node->index; - data->nearest.dist = calc_nearest_point(data, node, data->nearest.co); + data->nearest.dist = calc_nearest_point(data->proj, node, data->nearest.co); } } else @@ -1240,12 +1240,12 @@ static void dfs_find_nearest_dfs(BVHNearestData *data, BVHNode *node) int i; float nearest[3]; - if(data->proj[ (int)node->main_axis ] <= node->children[0]->bv[(int)node->main_axis*2+1]) + if(data->proj[ node->main_axis ] <= node->children[0]->bv[node->main_axis*2+1]) { for(i=0; i != node->totnode; i++) { - if( calc_nearest_point(data, node->children[i], nearest) >= data->nearest.dist) continue; + if( calc_nearest_point(data->proj, node->children[i], nearest) >= data->nearest.dist) continue; dfs_find_nearest_dfs(data, node->children[i]); } } @@ -1253,7 +1253,7 @@ static void dfs_find_nearest_dfs(BVHNearestData *data, BVHNode *node) { for(i=node->totnode-1; i >= 0 ; i--) { - if( calc_nearest_point(data, node->children[i], nearest) >= data->nearest.dist) continue; + if( calc_nearest_point(data->proj, node->children[i], nearest) >= data->nearest.dist) continue; dfs_find_nearest_dfs(data, node->children[i]); } } @@ -1263,7 +1263,7 @@ static void dfs_find_nearest_dfs(BVHNearestData *data, BVHNode *node) static void dfs_find_nearest_begin(BVHNearestData *data, BVHNode *node) { float nearest[3], sdist; - sdist = calc_nearest_point(data, node, nearest); + sdist = calc_nearest_point(data->proj, node, nearest); if(sdist >= data->nearest.dist) return; dfs_find_nearest_dfs(data, node); } @@ -1301,7 +1301,7 @@ static void bfs_find_nearest(BVHNearestData *data, BVHNode *node) } current.node = node; - current.dist = calc_nearest_point(data, node, nearest); + current.dist = calc_nearest_point(data->proj, node, nearest); while(current.dist < data->nearest.dist) { @@ -1329,7 +1329,7 @@ static void bfs_find_nearest(BVHNearestData *data, BVHNode *node) } heap[heap_size].node = current.node->children[i]; - heap[heap_size].dist = calc_nearest_point(data, current.node->children[i], nearest); + heap[heap_size].dist = calc_nearest_point(data->proj, current.node->children[i], nearest); if(heap[heap_size].dist >= data->nearest.dist) continue; heap_size++; @@ -1339,7 +1339,7 @@ static void bfs_find_nearest(BVHNearestData *data, BVHNode *node) push_heaps++; } } - + if(heap_size == 0) break; current = heap[0]; @@ -1355,6 +1355,7 @@ static void bfs_find_nearest(BVHNearestData *data, BVHNode *node) } #endif + int BLI_bvhtree_find_nearest(BVHTree *tree, const float *co, BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata) { int i; @@ -1435,7 +1436,7 @@ static float ray_nearest_hit(BVHRayCastData *data, float *bv) if(lu > low) low = lu; if(ll < upper) upper = ll; } - + if(low > upper) return FLT_MAX; } } @@ -1532,28 +1533,115 @@ float BLI_bvhtree_bb_raycast(float *bv, float *light_start, float *light_end, fl float dist = 0.0; data.hit.dist = FLT_MAX; - + // get light direction data.ray.direction[0] = light_end[0] - light_start[0]; data.ray.direction[1] = light_end[1] - light_start[1]; data.ray.direction[2] = light_end[2] - light_start[2]; - + data.ray.radius = 0.0; - + data.ray.origin[0] = light_start[0]; data.ray.origin[1] = light_start[1]; data.ray.origin[2] = light_start[2]; - + Normalize(data.ray.direction); VECCOPY(data.ray_dot_axis, data.ray.direction); - + dist = ray_nearest_hit(&data, bv); - + if(dist > 0.0) { VECADDFAC(pos, light_start, data.ray.direction, dist); } return dist; - + } +/* + * Range Query - as request by broken :P + * + * Allocs and fills an array with the indexs of node that are on the given spherical range (center, radius) + * Returns the size of the array. + */ +typedef struct RangeQueryData +{ + BVHTree *tree; + const float *center; + float radius; //squared radius + + int hits; + + BVHTree_RangeQuery callback; + void *userdata; + + +} RangeQueryData; + + +static void dfs_range_query(RangeQueryData *data, BVHNode *node) +{ + if(node->totnode == 0) + { + + //Calculate the node min-coords (if the node was a point then this is the point coordinates) + float co[3]; + co[0] = node->bv[0]; + co[1] = node->bv[2]; + co[2] = node->bv[4]; + + } + else + { + int i; + for(i=0; i != node->totnode; i++) + { + float nearest[3]; + float dist = calc_nearest_point(data->center, node->children[i], nearest); + if(dist < data->radius) + { + //Its a leaf.. call the callback + if(node->children[i]->totnode == 0) + { + data->hits++; + data->callback( data->userdata, node->children[i]->index, dist ); + } + else + dfs_range_query( data, node->children[i] ); + } + } + } +} + +int BLI_bvhtree_range_query(BVHTree *tree, const float *co, float radius, BVHTree_RangeQuery callback, void *userdata) +{ + BVHNode * root = tree->nodes[tree->totleaf]; + + RangeQueryData data; + data.tree = tree; + data.center = co; + data.radius = radius*radius; + data.hits = 0; + + data.callback = callback; + data.userdata = userdata; + + if(root != NULL) + { + float nearest[3]; + float dist = calc_nearest_point(data.center, root, nearest); + if(dist < data.radius) + { + //Its a leaf.. call the callback + if(root->totnode == 0) + { + data.hits++; + data.callback( data.userdata, root->index, dist ); + } + else + dfs_range_query( &data, root ); + } + } + + return data.hits; +} diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c index a26e333e095..ebead4bce9b 100644 --- a/source/blender/blenlib/intern/arithb.c +++ b/source/blender/blenlib/intern/arithb.c @@ -2176,6 +2176,13 @@ void VecSubf(float *v, float *v1, float *v2) v[2]= v1[2]- v2[2]; } +void VecMulVecf(float *v, float *v1, float *v2) +{ + v[0] = v1[0] * v2[0]; + v[1] = v1[1] * v2[1]; + v[2] = v1[2] * v2[2]; +} + void VecLerpf(float *target, float *a, float *b, float t) { float s = 1.0f-t; diff --git a/source/blender/blenlib/intern/voxel.c b/source/blender/blenlib/intern/voxel.c new file mode 100644 index 00000000000..b5b2ae793f9 --- /dev/null +++ b/source/blender/blenlib/intern/voxel.c @@ -0,0 +1,299 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Matt Ebb, Raul Fernandez Hernandez (Farsthary). + * + * ***** END GPL LICENSE BLOCK ***** + */ +#include + +#include "BLI_voxel.h" + +#include "BKE_utildefines.h" + + +#if defined( _MSC_VER ) && !defined( __cplusplus ) +# define inline __inline +#endif // defined( _MSC_VER ) && !defined( __cplusplus ) + +static inline float D(float *data, int *res, int x, int y, int z) +{ + CLAMP(x, 0, res[0]-1); + CLAMP(y, 0, res[1]-1); + CLAMP(z, 0, res[2]-1); + return data[ V_I(x, y, z, res) ]; +} + +/* *** nearest neighbour *** */ +/* input coordinates must be in bounding box 0.0 - 1.0 */ +float voxel_sample_nearest(float *data, int *res, float *co) +{ + int xi, yi, zi; + + xi = co[0] * res[0]; + yi = co[1] * res[1]; + zi = co[2] * res[2]; + + return D(data, res, xi, yi, zi); +} + + +/* *** trilinear *** */ +/* input coordinates must be in bounding box 0.0 - 1.0 */ + +static inline float lerp(float t, float v1, float v2) { + return (1.f - t) * v1 + t * v2; +} + +/* trilinear interpolation - taken partly from pbrt's implementation: http://www.pbrt.org */ +float voxel_sample_trilinear(float *data, int *res, float *co) +{ + float voxx, voxy, voxz; + int vx, vy, vz; + float dx, dy, dz; + float d00, d10, d01, d11, d0, d1, d_final; + + if (!data) return 0.f; + + voxx = co[0] * res[0] - 0.5f; + voxy = co[1] * res[1] - 0.5f; + voxz = co[2] * res[2] - 0.5f; + + vx = (int)voxx; vy = (int)voxy; vz = (int)voxz; + + dx = voxx - vx; dy = voxy - vy; dz = voxz - vz; + + d00 = lerp(dx, D(data, res, vx, vy, vz), D(data, res, vx+1, vy, vz)); + d10 = lerp(dx, D(data, res, vx, vy+1, vz), D(data, res, vx+1, vy+1, vz)); + d01 = lerp(dx, D(data, res, vx, vy, vz+1), D(data, res, vx+1, vy, vz+1)); + d11 = lerp(dx, D(data, res, vx, vy+1, vz+1), D(data, res, vx+1, vy+1, vz+1)); + d0 = lerp(dy, d00, d10); + d1 = lerp(dy, d01, d11); + d_final = lerp(dz, d0, d1); + + return d_final; +} + +/* *** tricubic *** */ + +int C[64][64] = { +{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{-3, 3, 0, 0, 0, 0, 0, 0,-2,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 2,-2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0,-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 9,-9,-9, 9, 0, 0, 0, 0, 6, 3,-6,-3, 0, 0, 0, 0, 6,-6, 3,-3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{-6, 6, 6,-6, 0, 0, 0, 0,-3,-3, 3, 3, 0, 0, 0, 0,-4, 4,-2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2,-2,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 2, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{-6, 6, 6,-6, 0, 0, 0, 0,-4,-2, 4, 2, 0, 0, 0, 0,-3, 3,-3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2,-1,-2,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 4,-4,-4, 4, 0, 0, 0, 0, 2, 2,-2,-2, 0, 0, 0, 0, 2,-2, 2,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 3, 0, 0, 0, 0, 0, 0,-2,-1, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,-2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-1, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,-9,-9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3,-6,-3, 0, 0, 0, 0, 6,-6, 3,-3, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-6, 6, 6,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3,-3, 3, 3, 0, 0, 0, 0,-4, 4,-2, 2, 0, 0, 0, 0,-2,-2,-1,-1, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-6, 6, 6,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-4,-2, 4, 2, 0, 0, 0, 0,-3, 3,-3, 3, 0, 0, 0, 0,-2,-1,-2,-1, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,-4,-4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,-2,-2, 0, 0, 0, 0, 2,-2, 2,-2, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}, +{-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0,-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 9,-9, 0, 0,-9, 9, 0, 0, 6, 3, 0, 0,-6,-3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,-6, 0, 0, 3,-3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{-6, 6, 0, 0, 6,-6, 0, 0,-3,-3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-4, 4, 0, 0,-2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2,-2, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0, 0, 0,-1, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,-9, 0, 0,-9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, 0, 0,-6,-3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,-6, 0, 0, 3,-3, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-6, 6, 0, 0, 6,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3,-3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-4, 4, 0, 0,-2, 2, 0, 0,-2,-2, 0, 0,-1,-1, 0, 0}, +{ 9, 0,-9, 0,-9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0,-6, 0,-3, 0, 6, 0,-6, 0, 3, 0,-3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 9, 0,-9, 0,-9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0,-6, 0,-3, 0, 6, 0,-6, 0, 3, 0,-3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0}, +{-27,27,27,-27,27,-27,-27,27,-18,-9,18, 9,18, 9,-18,-9,-18,18,-9, 9,18,-18, 9,-9,-18,18,18,-18,-9, 9, 9,-9,-12,-6,-6,-3,12, 6, 6, 3,-12,-6,12, 6,-6,-3, 6, 3,-12,12,-6, 6,-6, 6,-3, 3,-8,-4,-4,-2,-4,-2,-2,-1}, +{18,-18,-18,18,-18,18,18,-18, 9, 9,-9,-9,-9,-9, 9, 9,12,-12, 6,-6,-12,12,-6, 6,12,-12,-12,12, 6,-6,-6, 6, 6, 6, 3, 3,-6,-6,-3,-3, 6, 6,-6,-6, 3, 3,-3,-3, 8,-8, 4,-4, 4,-4, 2,-2, 4, 4, 2, 2, 2, 2, 1, 1}, +{-6, 0, 6, 0, 6, 0,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 0,-3, 0, 3, 0, 3, 0,-4, 0, 4, 0,-2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-2, 0,-1, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0,-6, 0, 6, 0, 6, 0,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 0,-3, 0, 3, 0, 3, 0,-4, 0, 4, 0,-2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-2, 0,-1, 0,-1, 0}, +{18,-18,-18,18,-18,18,18,-18,12, 6,-12,-6,-12,-6,12, 6, 9,-9, 9,-9,-9, 9,-9, 9,12,-12,-12,12, 6,-6,-6, 6, 6, 3, 6, 3,-6,-3,-6,-3, 8, 4,-8,-4, 4, 2,-4,-2, 6,-6, 6,-6, 3,-3, 3,-3, 4, 2, 4, 2, 2, 1, 2, 1}, +{-12,12,12,-12,12,-12,-12,12,-6,-6, 6, 6, 6, 6,-6,-6,-6, 6,-6, 6, 6,-6, 6,-6,-8, 8, 8,-8,-4, 4, 4,-4,-3,-3,-3,-3, 3, 3, 3, 3,-4,-4, 4, 4,-2,-2, 2, 2,-4, 4,-4, 4,-2, 2,-2, 2,-2,-2,-2,-2,-1,-1,-1,-1}, +{ 2, 0, 0, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{-6, 6, 0, 0, 6,-6, 0, 0,-4,-2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 3, 0, 0,-3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2,-1, 0, 0,-2,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 4,-4, 0, 0,-4, 4, 0, 0, 2, 2, 0, 0,-2,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,-2, 0, 0, 2,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-6, 6, 0, 0, 6,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-4,-2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 3, 0, 0,-3, 3, 0, 0,-2,-1, 0, 0,-2,-1, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,-4, 0, 0,-4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0,-2,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,-2, 0, 0, 2,-2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0}, +{-6, 0, 6, 0, 6, 0,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0,-4, 0,-2, 0, 4, 0, 2, 0,-3, 0, 3, 0,-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-1, 0,-2, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0,-6, 0, 6, 0, 6, 0,-6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-4, 0,-2, 0, 4, 0, 2, 0,-3, 0, 3, 0,-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 0,-1, 0,-2, 0,-1, 0}, +{18,-18,-18,18,-18,18,18,-18,12, 6,-12,-6,-12,-6,12, 6,12,-12, 6,-6,-12,12,-6, 6, 9,-9,-9, 9, 9,-9,-9, 9, 8, 4, 4, 2,-8,-4,-4,-2, 6, 3,-6,-3, 6, 3,-6,-3, 6,-6, 3,-3, 6,-6, 3,-3, 4, 2, 2, 1, 4, 2, 2, 1}, +{-12,12,12,-12,12,-12,-12,12,-6,-6, 6, 6, 6, 6,-6,-6,-8, 8,-4, 4, 8,-8, 4,-4,-6, 6, 6,-6,-6, 6, 6,-6,-4,-4,-2,-2, 4, 4, 2, 2,-3,-3, 3, 3,-3,-3, 3, 3,-4, 4,-2, 2,-4, 4,-2, 2,-2,-2,-1,-1,-2,-2,-1,-1}, +{ 4, 0,-4, 0,-4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,-2, 0,-2, 0, 2, 0,-2, 0, 2, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 4, 0,-4, 0,-4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,-2, 0,-2, 0, 2, 0,-2, 0, 2, 0,-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, +{-12,12,12,-12,12,-12,-12,12,-8,-4, 8, 4, 8, 4,-8,-4,-6, 6,-6, 6, 6,-6, 6,-6,-6, 6, 6,-6,-6, 6, 6,-6,-4,-2,-4,-2, 4, 2, 4, 2,-4,-2, 4, 2,-4,-2, 4, 2,-3, 3,-3, 3,-3, 3,-3, 3,-2,-1,-2,-1,-2,-1,-2,-1}, +{ 8,-8,-8, 8,-8, 8, 8,-8, 4, 4,-4,-4,-4,-4, 4, 4, 4,-4, 4,-4,-4, 4,-4, 4, 4,-4,-4, 4, 4,-4,-4, 4, 2, 2, 2, 2,-2,-2,-2,-2, 2, 2,-2,-2, 2, 2,-2,-2, 2,-2, 2,-2, 2,-2, 2,-2, 1, 1, 1, 1, 1, 1, 1, 1}}; + +static int ijk2n(int i, int j, int k) { + return(i+4*j+16*k); +} + +static void tricubic_get_coeff_stacked(float a[64], float x[64]) { + int i,j; + for (i=0;i<64;i++) { + a[i]=(float)(0.0); + for (j=0;j<64;j++) { + a[i]+=C[i][j]*x[j]; + } + } +} + + + + +static void tricubic_get_coeff(float a[64], float f[8], float dfdx[8], float dfdy[8], float dfdz[8], float d2fdxdy[8], float d2fdxdz[8], float d2fdydz[8], float d3fdxdydz[8]) { + int i; + float x[64]; + for (i=0;i<8;i++) { + x[0+i]=f[i]; + x[8+i]=dfdx[i]; + x[16+i]=dfdy[i]; + x[24+i]=dfdz[i]; + x[32+i]=d2fdxdy[i]; + x[40+i]=d2fdxdz[i]; + x[48+i]=d2fdydz[i]; + x[56+i]=d3fdxdydz[i]; + } + tricubic_get_coeff_stacked(a,x); +} + +static float tricubic_eval(float a[64], float x, float y, float z) { + int i,j,k; + float ret=(float)(0.0); + + for (i=0;i<4;i++) { + for (j=0;j<4;j++) { + for (k=0;k<4;k++) { + ret+=a[ijk2n(i,j,k)]*pow(x,i)*pow(y,j)*pow(z,k); + } + } + } + return(ret); +} + +/* tricubic interpolation + * from 'libtricubic': http://www.lekien.com/~francois/software/tricubic/ + * input coordinates must be in bounding box 0.0 - 1.0 */ +float voxel_sample_tricubic(float *data, int *res, float *co) +{ + float xx, yy, zz; + int xi,yi,zi; + int *n = res; + float dx,dy,dz; + float a[64]; + + xx = co[0] * res[0] - 0.5f; + yy = co[1] * res[1] - 0.5f; + zz = co[2] * res[2] - 0.5f; + + xi = (int)xx; yi = (int)yy; zi = (int)zz; + + { + float fval[8]={data[V_I(xi,yi,zi,n)],data[V_I(xi+1,yi,zi,n)],data[V_I(xi,yi+1,zi,n)],data[V_I(xi+1,yi+1,zi,n)],data[V_I(xi,yi,zi+1,n)],data[V_I(xi+1,yi,zi+1,n)],data[V_I(xi,yi+1,zi+1,n)],data[V_I(xi+1,yi+1,zi+1,n)]}; + + float dfdxval[8]={0.5f*(data[V_I(xi+1,yi,zi,n)]-data[V_I(xi-1,yi,zi,n)]),0.5f*(data[V_I(xi+2,yi,zi,n)]-data[V_I(xi,yi,zi,n)]), + 0.5f*(data[V_I(xi+1,yi+1,zi,n)]-data[V_I(xi-1,yi+1,zi,n)]),0.5f*(data[V_I(xi+2,yi+1,zi,n)]-data[V_I(xi,yi+1,zi,n)]), + 0.5f*(data[V_I(xi+1,yi,zi+1,n)]-data[V_I(xi-1,yi,zi+1,n)]),0.5f*(data[V_I(xi+2,yi,zi+1,n)]-data[V_I(xi,yi,zi+1,n)]), + 0.5f*(data[V_I(xi+1,yi+1,zi+1,n)]-data[V_I(xi-1,yi+1,zi+1,n)]), + 0.5f*(data[V_I(xi+2,yi+1,zi+1,n)]-data[V_I(xi,yi+1,zi+1,n)])}; + + float dfdyval[8]={0.5f*(data[V_I(xi,yi+1,zi,n)]-data[V_I(xi,yi-1,zi,n)]),0.5f*(data[V_I(xi+1,yi+1,zi,n)]-data[V_I(xi+1,yi-1,zi,n)]), + 0.5f*(data[V_I(xi,yi+2,zi,n)]-data[V_I(xi,yi,zi,n)]),0.5f*(data[V_I(xi+1,yi+2,zi,n)]-data[V_I(xi+1,yi,zi,n)]), + 0.5f*(data[V_I(xi,yi+1,zi+1,n)]-data[V_I(xi,yi-1,zi+1,n)]),0.5f*(data[V_I(xi+1,yi+1,zi+1,n)]-data[V_I(xi+1,yi-1,zi+1,n)]), + 0.5f*(data[V_I(xi,yi+2,zi+1,n)]-data[V_I(xi,yi,zi+1,n)]), + 0.5f*(data[V_I(xi+1,yi+2,zi+1,n)]-data[V_I(xi+1,yi,zi+1,n)])}; + + float dfdzval[8]={0.5f*(data[V_I(xi,yi,zi+1,n)]-data[V_I(xi,yi,zi-1,n)]),0.5f*(data[V_I(xi+1,yi,zi+1,n)]-data[V_I(xi+1,yi,zi-1,n)]), + 0.5f*(data[V_I(xi,yi+1,zi+1,n)]-data[V_I(xi,yi+1,zi-1,n)]),0.5f*(data[V_I(xi+1,yi+1,zi+1,n)]-data[V_I(xi+1,yi+1,zi-1,n)]), + 0.5f*(data[V_I(xi,yi,zi+2,n)]-data[V_I(xi,yi,zi,n)]),0.5f*(data[V_I(xi+1,yi,zi+2,n)]-data[V_I(xi+1,yi,zi,n)]), + 0.5f*(data[V_I(xi,yi+1,zi+2,n)]-data[V_I(xi,yi+1,zi,n)]), + 0.5f*(data[V_I(xi+1,yi+1,zi+2,n)]-data[V_I(xi+1,yi+1,zi,n)])}; + + float d2fdxdyval[8]={0.25*(data[V_I(xi+1,yi+1,zi,n)]-data[V_I(xi-1,yi+1,zi,n)]-data[V_I(xi+1,yi-1,zi,n)]+data[V_I(xi-1,yi-1,zi,n)]), + 0.25*(data[V_I(xi+2,yi+1,zi,n)]-data[V_I(xi,yi+1,zi,n)]-data[V_I(xi+2,yi-1,zi,n)]+data[V_I(xi,yi-1,zi,n)]), + 0.25*(data[V_I(xi+1,yi+2,zi,n)]-data[V_I(xi-1,yi+2,zi,n)]-data[V_I(xi+1,yi,zi,n)]+data[V_I(xi-1,yi,zi,n)]), + 0.25*(data[V_I(xi+2,yi+2,zi,n)]-data[V_I(xi,yi+2,zi,n)]-data[V_I(xi+2,yi,zi,n)]+data[V_I(xi,yi,zi,n)]), + 0.25*(data[V_I(xi+1,yi+1,zi+1,n)]-data[V_I(xi-1,yi+1,zi+1,n)]-data[V_I(xi+1,yi-1,zi+1,n)]+data[V_I(xi-1,yi-1,zi+1,n)]), + 0.25*(data[V_I(xi+2,yi+1,zi+1,n)]-data[V_I(xi,yi+1,zi+1,n)]-data[V_I(xi+2,yi-1,zi+1,n)]+data[V_I(xi,yi-1,zi+1,n)]), + 0.25*(data[V_I(xi+1,yi+2,zi+1,n)]-data[V_I(xi-1,yi+2,zi+1,n)]-data[V_I(xi+1,yi,zi+1,n)]+data[V_I(xi-1,yi,zi+1,n)]), + 0.25*(data[V_I(xi+2,yi+2,zi+1,n)]-data[V_I(xi,yi+2,zi+1,n)]-data[V_I(xi+2,yi,zi+1,n)]+data[V_I(xi,yi,zi+1,n)])}; + + float d2fdxdzval[8]={0.25f*(data[V_I(xi+1,yi,zi+1,n)]-data[V_I(xi-1,yi,zi+1,n)]-data[V_I(xi+1,yi,zi-1,n)]+data[V_I(xi-1,yi,zi-1,n)]), + 0.25f*(data[V_I(xi+2,yi,zi+1,n)]-data[V_I(xi,yi,zi+1,n)]-data[V_I(xi+2,yi,zi-1,n)]+data[V_I(xi,yi,zi-1,n)]), + 0.25f*(data[V_I(xi+1,yi+1,zi+1,n)]-data[V_I(xi-1,yi+1,zi+1,n)]-data[V_I(xi+1,yi+1,zi-1,n)]+data[V_I(xi-1,yi+1,zi-1,n)]), + 0.25f*(data[V_I(xi+2,yi+1,zi+1,n)]-data[V_I(xi,yi+1,zi+1,n)]-data[V_I(xi+2,yi+1,zi-1,n)]+data[V_I(xi,yi+1,zi-1,n)]), + 0.25f*(data[V_I(xi+1,yi,zi+2,n)]-data[V_I(xi-1,yi,zi+2,n)]-data[V_I(xi+1,yi,zi,n)]+data[V_I(xi-1,yi,zi,n)]), + 0.25f*(data[V_I(xi+2,yi,zi+2,n)]-data[V_I(xi,yi,zi+2,n)]-data[V_I(xi+2,yi,zi,n)]+data[V_I(xi,yi,zi,n)]), + 0.25f*(data[V_I(xi+1,yi+1,zi+2,n)]-data[V_I(xi-1,yi+1,zi+2,n)]-data[V_I(xi+1,yi+1,zi,n)]+data[V_I(xi-1,yi+1,zi,n)]), + 0.25f*(data[V_I(xi+2,yi+1,zi+2,n)]-data[V_I(xi,yi+1,zi+2,n)]-data[V_I(xi+2,yi+1,zi,n)]+data[V_I(xi,yi+1,zi,n)])}; + + + float d2fdydzval[8]={0.25f*(data[V_I(xi,yi+1,zi+1,n)]-data[V_I(xi,yi-1,zi+1,n)]-data[V_I(xi,yi+1,zi-1,n)]+data[V_I(xi,yi-1,zi-1,n)]), + 0.25f*(data[V_I(xi+1,yi+1,zi+1,n)]-data[V_I(xi+1,yi-1,zi+1,n)]-data[V_I(xi+1,yi+1,zi-1,n)]+data[V_I(xi+1,yi-1,zi-1,n)]), + 0.25f*(data[V_I(xi,yi+2,zi+1,n)]-data[V_I(xi,yi,zi+1,n)]-data[V_I(xi,yi+2,zi-1,n)]+data[V_I(xi,yi,zi-1,n)]), + 0.25f*(data[V_I(xi+1,yi+2,zi+1,n)]-data[V_I(xi+1,yi,zi+1,n)]-data[V_I(xi+1,yi+2,zi-1,n)]+data[V_I(xi+1,yi,zi-1,n)]), + 0.25f*(data[V_I(xi,yi+1,zi+2,n)]-data[V_I(xi,yi-1,zi+2,n)]-data[V_I(xi,yi+1,zi,n)]+data[V_I(xi,yi-1,zi,n)]), + 0.25f*(data[V_I(xi+1,yi+1,zi+2,n)]-data[V_I(xi+1,yi-1,zi+2,n)]-data[V_I(xi+1,yi+1,zi,n)]+data[V_I(xi+1,yi-1,zi,n)]), + 0.25f*(data[V_I(xi,yi+2,zi+2,n)]-data[V_I(xi,yi,zi+2,n)]-data[V_I(xi,yi+2,zi,n)]+data[V_I(xi,yi,zi,n)]), + 0.25f*(data[V_I(xi+1,yi+2,zi+2,n)]-data[V_I(xi+1,yi,zi+2,n)]-data[V_I(xi+1,yi+2,zi,n)]+data[V_I(xi+1,yi,zi,n)])}; + + + float d3fdxdydzval[8]={0.125f*(data[V_I(xi+1,yi+1,zi+1,n)]-data[V_I(xi-1,yi+1,zi+1,n)]-data[V_I(xi+1,yi-1,zi+1,n)]+data[V_I(xi-1,yi-1,zi+1,n)]-data[V_I(xi+1,yi+1,zi-1,n)]+data[V_I(xi-1,yi+1,zi-1,n)]+data[V_I(xi+1,yi-1,zi-1,n)]-data[V_I(xi-1,yi-1,zi-1,n)]), + 0.125f*(data[V_I(xi+2,yi+1,zi+1,n)]-data[V_I(xi,yi+1,zi+1,n)]-data[V_I(xi+2,yi-1,zi+1,n)]+data[V_I(xi,yi-1,zi+1,n)]-data[V_I(xi+2,yi+1,zi-1,n)]+data[V_I(xi,yi+1,zi-1,n)]+data[V_I(xi+2,yi-1,zi-1,n)]-data[V_I(xi,yi-1,zi-1,n)]), + 0.125f*(data[V_I(xi+1,yi+2,zi+1,n)]-data[V_I(xi-1,yi+2,zi+1,n)]-data[V_I(xi+1,yi,zi+1,n)]+data[V_I(xi-1,yi,zi+1,n)]-data[V_I(xi+1,yi+2,zi-1,n)]+data[V_I(xi-1,yi+2,zi-1,n)]+data[V_I(xi+1,yi,zi-1,n)]-data[V_I(xi-1,yi,zi-1,n)]), + 0.125f*(data[V_I(xi+2,yi+2,zi+1,n)]-data[V_I(xi,yi+2,zi+1,n)]-data[V_I(xi+2,yi,zi+1,n)]+data[V_I(xi,yi,zi+1,n)]-data[V_I(xi+2,yi+2,zi-1,n)]+data[V_I(xi,yi+2,zi-1,n)]+data[V_I(xi+2,yi,zi-1,n)]-data[V_I(xi,yi,zi-1,n)]), + 0.125f*(data[V_I(xi+1,yi+1,zi+2,n)]-data[V_I(xi-1,yi+1,zi+2,n)]-data[V_I(xi+1,yi-1,zi+2,n)]+data[V_I(xi-1,yi-1,zi+2,n)]-data[V_I(xi+1,yi+1,zi,n)]+data[V_I(xi-1,yi+1,zi,n)]+data[V_I(xi+1,yi-1,zi,n)]-data[V_I(xi-1,yi-1,zi,n)]), + 0.125f*(data[V_I(xi+2,yi+1,zi+2,n)]-data[V_I(xi,yi+1,zi+2,n)]-data[V_I(xi+2,yi-1,zi+2,n)]+data[V_I(xi,yi-1,zi+2,n)]-data[V_I(xi+2,yi+1,zi,n)]+data[V_I(xi,yi+1,zi,n)]+data[V_I(xi+2,yi-1,zi,n)]-data[V_I(xi,yi-1,zi,n)]), + 0.125f*(data[V_I(xi+1,yi+2,zi+2,n)]-data[V_I(xi-1,yi+2,zi+2,n)]-data[V_I(xi+1,yi,zi+2,n)]+data[V_I(xi-1,yi,zi+2,n)]-data[V_I(xi+1,yi+2,zi,n)]+data[V_I(xi-1,yi+2,zi,n)]+data[V_I(xi+1,yi,zi,n)]-data[V_I(xi-1,yi,zi,n)]), + 0.125f*(data[V_I(xi+2,yi+2,zi+2,n)]-data[V_I(xi,yi+2,zi+2,n)]-data[V_I(xi+2,yi,zi+2,n)]+data[V_I(xi,yi,zi+2,n)]-data[V_I(xi+2,yi+2,zi,n)]+data[V_I(xi,yi+2,zi,n)]+data[V_I(xi+2,yi,zi,n)]-data[V_I(xi,yi,zi,n)])}; + + + tricubic_get_coeff(a,fval,dfdxval,dfdyval,dfdzval,d2fdxdyval,d2fdxdzval,d2fdydzval,d3fdxdydzval); + } + + dx = xx-xi; + dy = yy-yi; + dz = zz-zi; + + return tricubic_eval(a,dx,dy,dz); + +} + diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 77b51d950b9..5cc3ff7695a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2819,6 +2819,11 @@ static void lib_link_texture(FileData *fd, Main *main) tex->ima= newlibadr_us(fd, tex->id.lib, tex->ima); tex->ipo= newlibadr_us(fd, tex->id.lib, tex->ipo); if(tex->env) tex->env->object= newlibadr(fd, tex->id.lib, tex->env->object); + if(tex->pd) { + tex->pd->object= newlibadr(fd, tex->id.lib, tex->pd->object); + tex->pd->psys= newlibadr(fd, tex->id.lib, tex->pd->psys); + } + if(tex->vd) tex->vd->object= newlibadr(fd, tex->id.lib, tex->vd->object); if(tex->nodetree) lib_link_ntree(fd, &tex->id, tex->nodetree); @@ -2851,6 +2856,16 @@ static void direct_link_texture(FileData *fd, Tex *tex) memset(tex->env->cube, 0, 6*sizeof(void *)); tex->env->ok= 0; } + tex->pd= newdataadr(fd, tex->pd); + if(tex->pd) { + tex->pd->point_tree = NULL; + tex->pd->coba= newdataadr(fd, tex->pd->coba); + } + + tex->vd= newdataadr(fd, tex->vd); + if(tex->vd) { + tex->vd->dataset = NULL; + } tex->nodetree= newdataadr(fd, tex->nodetree); if(tex->nodetree) @@ -9478,6 +9493,24 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } } + + /* volume rendering settings */ + if (ma->vol.stepsize < 0.0001f) { + ma->vol.density = 1.0f; + ma->vol.emission = 0.0f; + ma->vol.absorption = 1.0f; + ma->vol.scattering = 1.0f; + ma->vol.emission_col[0] = ma->vol.emission_col[1] = ma->vol.emission_col[2] = 1.0f; + ma->vol.absorption_col[0] = ma->vol.absorption_col[1] = ma->vol.absorption_col[2] = 0.0f; + ma->vol.density_scale = 1.0f; + ma->vol.depth_cutoff = 0.01f; + ma->vol.stepsize_type = MA_VOL_STEP_RANDOMIZED; + ma->vol.stepsize = 0.2f; + ma->vol.shade_stepsize = 0.2f; + ma->vol.shade_type = MA_VOL_SHADE_SINGLE; + ma->vol.shadeflag |= MA_VOL_PRECACHESHADING; + ma->vol.precache_resolution = 50; + } } for(sce = main->scene.first; sce; sce = sce->id.next) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 31fe0c01900..eb3f17f368c 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1572,6 +1572,11 @@ static void write_textures(WriteData *wd, ListBase *idbase) if(tex->type == TEX_PLUGIN && tex->plugin) writestruct(wd, DATA, "PluginTex", 1, tex->plugin); if(tex->coba) writestruct(wd, DATA, "ColorBand", 1, tex->coba); if(tex->type == TEX_ENVMAP && tex->env) writestruct(wd, DATA, "EnvMap", 1, tex->env); + if(tex->type == TEX_POINTDENSITY && tex->pd) { + writestruct(wd, DATA, "PointDensity", 1, tex->pd); + if(tex->pd->coba) writestruct(wd, DATA, "ColorBand", 1, tex->pd->coba); + } + if(tex->type == TEX_VOXELDATA && tex->vd) writestruct(wd, DATA, "VoxelData", 1, tex->vd); /* nodetree is integral part of texture, no libdata */ if(tex->nodetree) { diff --git a/source/blender/editors/preview/previewrender.c b/source/blender/editors/preview/previewrender.c index 94db74c18d9..d17391811bb 100644 --- a/source/blender/editors/preview/previewrender.c +++ b/source/blender/editors/preview/previewrender.c @@ -307,6 +307,8 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre /* turn on raytracing if needed */ if(mat->mode_l & MA_RAYMIRROR) sce->r.mode |= R_RAYTRACE; + if(mat->material_type == MA_TYPE_VOLUME) + sce->r.mode |= R_RAYTRACE; if((mat->mode_l & MA_RAYTRANSP) && (mat->mode_l & MA_TRANSP)) sce->r.mode |= R_RAYTRACE; if(mat->sss_flag & MA_DIFF_SSS) diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index f7b3273c2e1..f268c117065 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -47,6 +47,34 @@ struct Ipo; /* WATCH IT: change type? also make changes in ipo.h */ +typedef struct VolumeSettings { + float density; + float emission; + float absorption; + float scattering; + + float emission_col[3]; + float absorption_col[3]; + float density_scale; + float depth_cutoff; + + short phasefunc_type; + short vpad[3]; + float phasefunc_g; + + float stepsize; + float shade_stepsize; + + short stepsize_type; + short shadeflag; + short shade_type; + short precache_resolution; + + float ms_diff; + float ms_intensity; + int ms_steps; +} VolumeSettings; + typedef struct Material { ID id; struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */ @@ -62,6 +90,8 @@ typedef struct Material { float translucency; /* end synced with render_types.h */ + struct VolumeSettings vol; + float fresnel_mir, fresnel_mir_i; float fresnel_tra, fresnel_tra_i; float filter; /* filter added, for raytrace transparency and transmissivity */ @@ -287,6 +317,14 @@ typedef struct Material { #define MAP_WARP 8192 #define MAP_LAYER 16384 +/* volume mapto - reuse definitions for now - a bit naughty! */ +#define MAP_DENSITY 128 +#define MAP_EMISSION 64 +#define MAP_EMISSION_COL 1 +#define MAP_ABSORPTION 512 +#define MAP_ABSORPTION_COL 8 +#define MAP_SCATTERING 16 + /* mapto for halo */ //#define MAP_HA_COL 1 //#define MAP_HA_ALPHA 128 @@ -330,5 +368,29 @@ typedef struct Material { /* sss_flag */ #define MA_DIFF_SSS 1 +/* vol_stepsize_type */ +#define MA_VOL_STEP_RANDOMIZED 0 +#define MA_VOL_STEP_CONSTANT 1 +#define MA_VOL_STEP_ADAPTIVE 2 + +/* vol_shadeflag */ +#define MA_VOL_SHADED 1 +#define MA_VOL_RECVSHADOW 4 +#define MA_VOL_PRECACHESHADING 8 + +/* vol_shading_type */ +#define MA_VOL_SHADE_NONE 0 +#define MA_VOL_SHADE_SINGLE 1 +#define MA_VOL_SHADE_MULTIPLE 2 +#define MA_VOL_SHADE_SINGLEPLUSMULTIPLE 3 + +/* vol_phasefunc_type */ +#define MA_VOL_PH_ISOTROPIC 0 +#define MA_VOL_PH_MIEHAZY 1 +#define MA_VOL_PH_MIEMURKY 2 +#define MA_VOL_PH_RAYLEIGH 3 +#define MA_VOL_PH_HG 4 +#define MA_VOL_PH_SCHLICK 5 + #endif diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 0054e885a21..1b6ed1bc032 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -128,6 +128,55 @@ typedef struct EnvMap { short recalc, lastsize; } EnvMap; +typedef struct PointDensity { + short flag; + + short falloff_type; + float falloff_softness; + float radius; + short source; + short color_source; + int totpoints; + + int pdpad; + + struct Object *object; /* for 'Object' or 'Particle system' type - source object */ + struct ParticleSystem *psys; + short psys_cache_space; /* cache points in worldspace, object space, ... ? */ + short ob_cache_space; /* cache points in worldspace, object space, ... ? */ + + short pdpad2[2]; + + void *point_tree; /* the acceleration tree containing points */ + float *point_data; /* dynamically allocated extra for extra information, like particle age */ + + float noise_size; + short noise_depth; + short noise_influence; + short noise_basis; + short pdpad3[3]; + float noise_fac; + + float speed_scale; + struct ColorBand *coba; /* for time -> color */ + +} PointDensity; + +typedef struct VoxelData { + int resol[3]; + int interp_type; + short file_format; + short flag; + int pad; + + struct Object *object; /* for rendering smoke sims */ + float int_multiplier; + int still_frame; + char source_path[240]; + float *dataset; + +} VoxelData; + typedef struct Tex { ID id; struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */ @@ -181,6 +230,8 @@ typedef struct Tex { struct ColorBand *coba; struct EnvMap *env; struct PreviewImage * preview; + struct PointDensity *pd; + struct VoxelData *vd; char use_nodes; char pad[7]; @@ -220,6 +271,8 @@ typedef struct TexMapping { #define TEX_MUSGRAVE 11 #define TEX_VORONOI 12 #define TEX_DISTNOISE 13 +#define TEX_POINTDENSITY 14 +#define TEX_VOXELDATA 15 /* musgrave stype */ #define TEX_MFRACTAL 0 @@ -417,5 +470,59 @@ typedef struct TexMapping { #define ENV_NORMAL 1 #define ENV_OSA 2 +/* **************** PointDensity ********************* */ + +/* source */ +#define TEX_PD_PSYS 0 +#define TEX_PD_OBJECT 1 +#define TEX_PD_FILE 2 + +/* falloff_type */ +#define TEX_PD_FALLOFF_STD 0 +#define TEX_PD_FALLOFF_SMOOTH 1 +#define TEX_PD_FALLOFF_SOFT 2 +#define TEX_PD_FALLOFF_CONSTANT 3 +#define TEX_PD_FALLOFF_ROOT 4 + +/* psys_cache_space */ +#define TEX_PD_OBJECTLOC 0 +#define TEX_PD_OBJECTSPACE 1 +#define TEX_PD_WORLDSPACE 2 + +/* flag */ +#define TEX_PD_TURBULENCE 1 + + +/* noise_influence */ +#define TEX_PD_NOISE_STATIC 0 +#define TEX_PD_NOISE_VEL 1 +#define TEX_PD_NOISE_AGE 2 +#define TEX_PD_NOISE_TIME 3 + +/* color_source */ +#define TEX_PD_COLOR_CONSTANT 0 +#define TEX_PD_COLOR_PARTAGE 1 +#define TEX_PD_COLOR_PARTSPEED 2 +#define TEX_PD_COLOR_PARTVEL 3 + +#define POINT_DATA_VEL 1 +#define POINT_DATA_LIFE 2 + +/******************** Voxel Data *****************************/ +/* flag */ +#define TEX_VD_STILL 1 + +/* interpolation */ +#define TEX_VD_NEARESTNEIGHBOR 0 +#define TEX_VD_LINEAR 1 +#define TEX_VD_TRICUBIC 2 + +/* file format */ +#define TEX_VD_BLENDERVOXEL 0 +#define TEX_VD_RAW_8BIT 1 +#define TEX_VD_RAW_16BIT 2 +#define TEX_VD_IMAGE_SEQUENCE 3 +#define TEX_VD_SMOKE 4 + #endif diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index e4fe92317d4..fb351efed66 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -321,6 +321,8 @@ extern StructRNA RNA_ParticleTarget; extern StructRNA RNA_PluginSequence; extern StructRNA RNA_PluginTexture; extern StructRNA RNA_PointCache; +extern StructRNA RNA_PointDensity; +extern StructRNA RNA_PointDensityTexture; extern StructRNA RNA_PointerProperty; extern StructRNA RNA_Pose; extern StructRNA RNA_PoseChannel; @@ -499,6 +501,8 @@ extern StructRNA RNA_VertexGroup; extern StructRNA RNA_VertexGroupElement; extern StructRNA RNA_VertexPaint; extern StructRNA RNA_VoronoiTexture; +extern StructRNA RNA_VoxelData; +extern StructRNA RNA_VoxelDataTexture; extern StructRNA RNA_WaveModifier; extern StructRNA RNA_Window; extern StructRNA RNA_WindowManager; diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 5ebaeb1da8d..cde65f46e5c 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -479,6 +479,75 @@ static void rna_def_material_mtex(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Ambient Factor", "Amount texture affects ambient."); RNA_def_property_update(prop, NC_TEXTURE, NULL); + /* volume material */ + prop= RNA_def_property(srna, "map_coloremission", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", MAP_EMISSION_COL); + RNA_def_property_ui_text(prop, "Emission Color", "Causes the texture to affect the colour of emission"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "map_colorabsorption", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", MAP_ABSORPTION_COL); + RNA_def_property_ui_text(prop, "Absorption Color", "Causes the texture to affect the result colour after absorption"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "map_density", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", MAP_DENSITY); + RNA_def_property_ui_text(prop, "Density", "Causes the texture to affect the volume's density"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "map_emission", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", MAP_EMISSION); + RNA_def_property_ui_text(prop, "Emission", "Causes the texture to affect the volume's emission"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "map_absorption", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", MAP_ABSORPTION); + RNA_def_property_ui_text(prop, "Absorption", "Causes the texture to affect the volume's absorption"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "map_scattering", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", MAP_SCATTERING); + RNA_def_property_ui_text(prop, "Scattering", "Causes the texture to affect the volume's scattering"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "coloremission_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "colfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Emission Color Factor", "Amount texture affects emission color."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "colorabsorption_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "colfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Absorpion Color Factor", "Amount texture affects diffuse color."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "density_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "varfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Density Factor", "Amount texture affects density."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "emission_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "varfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Emission Factor", "Amount texture affects emission."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "absorption_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "varfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Absorption Factor", "Amount texture affects absorption."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "scattering_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "varfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Scattering Factor", "Amount texture affects scattering."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + /* end volume material */ + prop= RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_MaterialTextureSlot_enabled_get", "rna_MaterialTextureSlot_enabled_set"); RNA_def_property_ui_text(prop, "Enabled", "Enable this material texture slot."); @@ -809,6 +878,159 @@ static void rna_def_material_raytra(BlenderRNA *brna) RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); } +static void rna_def_material_volume(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem prop_scattering_items[] = { + {MA_VOL_SHADE_NONE, "NONE", 0, "None", ""}, + {MA_VOL_SHADE_SINGLE, "SINGLE_SCATTERING", 0, "Single Scattering", ""}, + {MA_VOL_SHADE_MULTIPLE, "MULTIPLE_SCATTERING", 0, "Multiple Scattering", ""}, + {MA_VOL_SHADE_SINGLEPLUSMULTIPLE, "SINGLE_PLUS_MULTIPLE_SCATTERING", 0, "Single + Multiple Scattering", ""}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem prop_stepsize_items[] = { + {MA_VOL_STEP_RANDOMIZED, "RANDOMIZED", 0, "Randomized", ""}, + {MA_VOL_STEP_CONSTANT, "CONSTANT", 0, "Constant", ""}, + //{MA_VOL_STEP_ADAPTIVE, "ADAPTIVE", 0, "Adaptive", ""}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem prop_phasefunction_items[] = { + {MA_VOL_PH_ISOTROPIC, "ISOTROPIC", 0, "Isotropic", ""}, + {MA_VOL_PH_MIEHAZY, "MIE_HAZY", 0, "Mie Hazy", ""}, + {MA_VOL_PH_MIEMURKY, "MIE_MURKY", 0, "Mie Murky", ""}, + {MA_VOL_PH_RAYLEIGH, "RAYLEIGH", 0, "Rayleigh", ""}, + {MA_VOL_PH_HG, "HENYEY-GREENSTEIN", 0, "Henyey-Greenstein", ""}, + {MA_VOL_PH_SCHLICK, "SCHLICK", 0, "Schlick", ""}, + {0, NULL, 0, NULL, NULL}}; + + srna= RNA_def_struct(brna, "MaterialVolume", NULL); + RNA_def_struct_sdna(srna, "VolumeSettings"); + RNA_def_struct_nested(brna, srna, "Material"); + RNA_def_struct_ui_text(srna, "Material Volume", "Volume rendering settings for a Material datablock."); + + prop= RNA_def_property(srna, "step_calculation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "stepsize_type"); + RNA_def_property_enum_items(prop, prop_stepsize_items); + RNA_def_property_ui_text(prop, "Step Calculation", "Method of calculating the steps through the volume"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "step_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "stepsize"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 3); + RNA_def_property_ui_text(prop, "Step Size", "Distance between subsequent volume depth samples."); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "shading_step_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "shade_stepsize"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 3); + RNA_def_property_ui_text(prop, "Shading Step Size", "Distance between subsequent volume shading samples."); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "scattering_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "shade_type"); + RNA_def_property_enum_items(prop, prop_scattering_items); + RNA_def_property_ui_text(prop, "Scattering Mode", "Method of shading, attenuating, and scattering light through the volume"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "light_cache", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shadeflag", MA_VOL_PRECACHESHADING); /* use bitflags */ + RNA_def_property_ui_text(prop, "Light Cache", "Pre-calculate the shading information into a voxel grid, speeds up shading at slightly less accuracy"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "cache_resolution", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "precache_resolution"); + RNA_def_property_range(prop, 0, 1024); + RNA_def_property_ui_text(prop, "Resolution", "Resolution of the voxel grid, low resolutions are faster, high resolutions use more memory."); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "ms_diffusion", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ms_diff"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Diffusion", "Diffusion factor, the strength of the blurring effect"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "ms_spread", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "ms_steps"); + RNA_def_property_range(prop, 0, 1024); + RNA_def_property_ui_text(prop, "Spread", "Simulation steps, the effective distance over which the light is diffused"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "ms_intensity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ms_intensity"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Intensity", "Multiplier for multiple scattered light energy"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "depth_cutoff", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "depth_cutoff"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Depth Cutoff", "Stop ray marching early if transmission drops below this luminance - higher values give speedups in dense volumes at the expense of accuracy."); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "density", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_float_sdna(prop, NULL, "density"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Density", "The base density of the volume"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "density_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "density_scale"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3); + RNA_def_property_ui_text(prop, "Density Scale", "Multiplier for the material's density"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "absorption", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "absorption"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3); + RNA_def_property_ui_text(prop, "Absorption", "Amount of light that gets absorbed by the volume - higher values mean light travels less distance"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "absorption_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "absorption_col"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Absorption Color", ""); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING_DRAW, NULL); + + prop= RNA_def_property(srna, "scattering", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scattering"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1 ,3); + RNA_def_property_ui_text(prop, "Scattering", "Amount of light that gets scattered by the volume - values > 1.0 are non-physical"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "emission", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "emission"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 10.0f, 1, 3); + RNA_def_property_ui_text(prop, "Emission", "Amount of light that gets emitted by the volume"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "emission_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "emission_col"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Emission Color", ""); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING_DRAW, NULL); + + prop= RNA_def_property(srna, "phase_function", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "phasefunc_type"); + RNA_def_property_enum_items(prop, prop_phasefunction_items); + RNA_def_property_ui_text(prop, "Phase Function", "Isotropic/Anisotropic scattering"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); + + prop= RNA_def_property(srna, "asymmetry", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "phasefunc_g"); + RNA_def_property_range(prop, -1.0f, 1.0f); + RNA_def_property_ui_text(prop, "Asymmetry", "Continuum between forward scattering and back scattering"); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING, NULL); +} + + static void rna_def_material_halo(BlenderRNA *brna) { StructRNA *srna; @@ -1182,7 +1404,7 @@ void RNA_def_material(BlenderRNA *brna) static EnumPropertyItem prop_type_items[] = { {MA_TYPE_SURFACE, "SURFACE", 0, "Surface", "Render object as a surface."}, {MA_TYPE_WIRE, "WIRE", 0, "Wire", "Render the edges of faces as wires (not supported in ray tracing)."}, - // {MA_TYPE_VOLUME, "VOLUME", 0, "Volume", "Render object as a volume."}, + {MA_TYPE_VOLUME, "VOLUME", 0, "Volume", "Render object as a volume."}, {MA_TYPE_HALO, "HALO", 0, "Halo", "Render object as halo particles."}, {0, NULL, 0, NULL, NULL}}; static EnumPropertyItem transparency_items[] = { @@ -1365,6 +1587,11 @@ void RNA_def_material(BlenderRNA *brna) RNA_def_property_pointer_funcs(prop, "rna_Material_transp_get", NULL, NULL); RNA_def_property_ui_text(prop, "Raytrace Transparency", "Raytraced reflection settings for the material."); + prop= RNA_def_property(srna, "volume", PROP_POINTER, PROP_NEVER_NULL); + RNA_def_property_pointer_sdna(prop, NULL, "vol"); + RNA_def_property_struct_type(prop, "MaterialVolume"); + RNA_def_property_ui_text(prop, "Volume", "Volume settings for the material."); + prop= RNA_def_property(srna, "halo", PROP_POINTER, PROP_NEVER_NULL); RNA_def_property_struct_type(prop, "MaterialHalo"); RNA_def_property_pointer_funcs(prop, "rna_Material_halo_get", NULL, NULL); @@ -1408,6 +1635,7 @@ void RNA_def_material(BlenderRNA *brna) /* nested structs */ rna_def_material_raymirror(brna); rna_def_material_raytra(brna); + rna_def_material_volume(brna); rna_def_material_halo(brna); rna_def_material_sss(brna); rna_def_material_mtex(brna); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 69298fbb8a4..a949d26f51a 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -86,11 +86,32 @@ StructRNA *rna_Texture_refine(struct PointerRNA *ptr) return &RNA_VoronoiTexture; case TEX_DISTNOISE: return &RNA_DistortedNoiseTexture; + case TEX_POINTDENSITY: + return &RNA_PointDensityTexture; + case TEX_VOXELDATA: + return &RNA_VoxelDataTexture; default: return &RNA_Texture; } } +static void rna_Texture_type_set(PointerRNA *ptr, int value) +{ + Tex *tex= (Tex*)ptr->data; + + if (value == TEX_VOXELDATA) { + if (tex->vd == NULL) { + tex->vd = BKE_add_voxeldata(); + } + } else if (value == TEX_POINTDENSITY) { + if (tex->pd == NULL) { + tex->pd = BKE_add_pointdensity(); + } + } + + tex->type = value; +} + static int rna_TextureSlot_name_length(PointerRNA *ptr) { MTex *mtex= ptr->data; @@ -1304,6 +1325,200 @@ static void rna_def_texture_distorted_noise(BlenderRNA *brna) RNA_def_property_update(prop, NC_TEXTURE, NULL); } +static void rna_def_texture_pointdensity(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem point_source_items[] = { + {TEX_PD_PSYS, "PARTICLE_SYSTEM", 0, "Particle System", "Generate point density from a particle system"}, + {TEX_PD_OBJECT, "OBJECT", 0, "Object Vertices", "Generate point density from an object's vertices"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem falloff_items[] = { + {TEX_PD_FALLOFF_STD, "STANDARD", 0, "Standard", ""}, + {TEX_PD_FALLOFF_SMOOTH, "SMOOTH", 0, "Smooth", ""}, + {TEX_PD_FALLOFF_SOFT, "SOFT", 0, "Soft", ""}, + {TEX_PD_FALLOFF_CONSTANT, "CONSTANT", 0, "Constant", "Density is constant within lookup radius"}, + {TEX_PD_FALLOFF_ROOT, "ROOT", 0, "Root", ""}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem color_source_items[] = { + {TEX_PD_COLOR_CONSTANT, "CONSTANT", 0, "Constant", ""}, + {TEX_PD_COLOR_PARTAGE, "PARTICLE_AGE", 0, "Particle Age", "Lifetime mapped as 0.0 - 1.0 intensity"}, + {TEX_PD_COLOR_PARTSPEED, "PARTICLE_SPEED", 0, "Particle Speed", "Particle speed (absolute magnitude of velocity) mapped as 0.0-1.0 intensity"}, + {TEX_PD_COLOR_PARTVEL, "PARTICLE_VELOCITY", 0, "Particle Velocity", "XYZ velocity mapped to RGB colors"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem turbulence_influence_items[] = { + {TEX_PD_NOISE_STATIC, "STATIC", 0, "Static", "Noise patterns will remain unchanged, faster and suitable for stills"}, + {TEX_PD_NOISE_VEL, "PARTICLE_VELOCITY", 0, "Particle Velocity", "Turbulent noise driven by particle velocity"}, + {TEX_PD_NOISE_AGE, "PARTICLE_AGE", 0, "Particle Age", "Turbulent noise driven by the particle's age between birth and death"}, + {TEX_PD_NOISE_TIME, "GLOBAL_TIME", 0, "Global Time", "Turbulent noise driven by the global current frame"}, + {0, NULL, 0, NULL, NULL}}; + + srna= RNA_def_struct(brna, "PointDensity", NULL); + RNA_def_struct_sdna(srna, "PointDensity"); + RNA_def_struct_ui_text(srna, "PointDensity", "Point density settings."); + + prop= RNA_def_property(srna, "point_source", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "source"); + RNA_def_property_enum_items(prop, point_source_items); + RNA_def_property_ui_text(prop, "Point Source", "Point data to use as renderable point density"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "object"); + RNA_def_property_ui_text(prop, "Object", "Object to take point data from"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "psys"); + RNA_def_property_ui_text(prop, "Particle System", "Particle System to render as points"); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "radius", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "radius"); + RNA_def_property_range(prop, 0.01, FLT_MAX); + RNA_def_property_ui_text(prop, "Radius", "Radius from the shaded sample to look for points within"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "falloff", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "falloff_type"); + RNA_def_property_enum_items(prop, falloff_items); + RNA_def_property_ui_text(prop, "Falloff", "Method of attenuating density by distance from the point"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "falloff_softness", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "falloff_softness"); + RNA_def_property_range(prop, 0.01, FLT_MAX); + RNA_def_property_ui_text(prop, "Softness", "Softness of the 'soft' falloff option"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "color_source", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "color_source"); + RNA_def_property_enum_items(prop, color_source_items); + RNA_def_property_ui_text(prop, "Color Source", "Data to derive color results from"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "turbulence", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TEX_PD_TURBULENCE); + RNA_def_property_ui_text(prop, "Turbulence", "Add directed noise to the density at render-time"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "turbulence_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "noise_size"); + RNA_def_property_range(prop, 0.01, FLT_MAX); + RNA_def_property_ui_text(prop, "Size", "Scale of the added turbulent noise"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "turbulence_depth", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "noise_depth"); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Depth", "Level of detail in the added turbulent noise"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "turbulence_influence", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "noise_influence"); + RNA_def_property_enum_items(prop, turbulence_influence_items); + RNA_def_property_ui_text(prop, "Turbulence Influence", "Method for driving added turbulent noise"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + + + srna= RNA_def_struct(brna, "PointDensityTexture", "Texture"); + RNA_def_struct_sdna(srna, "Tex"); + RNA_def_struct_ui_text(srna, "Point Density", "Settings for the Point Density texture"); + + prop= RNA_def_property(srna, "pointdensity", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "pd"); + RNA_def_property_struct_type(prop, "PointDensity"); + RNA_def_property_ui_text(prop, "Point Density", "The point density settings associated with this texture"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); +} + +static void rna_def_texture_voxeldata(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem interpolation_type_items[] = { + {TEX_VD_NEARESTNEIGHBOR, "NEREASTNEIGHBOR", 0, "Nearest Neighbor", "No interpolation, fast but blocky and low quality."}, + {TEX_VD_LINEAR, "TRILINEAR", 0, "Trilinear", "Good smoothness and speed"}, + {TEX_VD_TRICUBIC, "TRICUBIC", 0, "Tricubic", "High quality interpolation, but slow"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem file_format_items[] = { + {TEX_VD_BLENDERVOXEL, "BLENDER_VOXEL", 0, "Blender Voxel", "Default binary voxel file format"}, + {TEX_VD_RAW_8BIT, "RAW_8BIT", 0, "8 bit RAW", "8 bit greyscale binary data"}, + {TEX_VD_IMAGE_SEQUENCE, "IMAGE_SEQUENCE", 0, "Image Sequence", "Generate voxels from a sequence of image slices"}, + {TEX_VD_SMOKE, "SMOKE", 0, "Smoke", "Render voxels from a Blender smoke simulation"}, + {0, NULL, 0, NULL, NULL}}; + + srna= RNA_def_struct(brna, "VoxelData", NULL); + RNA_def_struct_sdna(srna, "VoxelData"); + RNA_def_struct_ui_text(srna, "VoxelData", "Voxel data settings."); + + prop= RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "interp_type"); + RNA_def_property_enum_items(prop, interpolation_type_items); + RNA_def_property_ui_text(prop, "Interpolation", "Method to interpolate/smooth values between voxel cells"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "intensity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "int_multiplier"); + RNA_def_property_range(prop, 0.01, FLT_MAX); + RNA_def_property_ui_text(prop, "Intensity", "Multiplier for intensity values"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "file_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "file_format"); + RNA_def_property_enum_items(prop, file_format_items); + RNA_def_property_ui_text(prop, "File Format", "Format of the source data set to render "); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "source_path", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "source_path"); + RNA_def_property_ui_text(prop, "Source Path", "The external source data file to use"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "resol"); + RNA_def_property_ui_text(prop, "Resolution", "Resolution of the voxel grid."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "still", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TEX_VD_STILL); + RNA_def_property_ui_text(prop, "Still Frame Only", "Always render a still frame from the voxel data sequence"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "still_frame_number", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "still_frame"); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Still Frame Number", "The frame number to always use"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "domain_object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "object"); + RNA_def_property_ui_text(prop, "Domain Object", "Object used as the smoke simulation domain"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + + srna= RNA_def_struct(brna, "VoxelDataTexture", "Texture"); + RNA_def_struct_sdna(srna, "Tex"); + RNA_def_struct_ui_text(srna, "Voxel Data", "Settings for the Voxel Data texture"); + + prop= RNA_def_property(srna, "voxeldata", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "vd"); + RNA_def_property_struct_type(prop, "VoxelData"); + RNA_def_property_ui_text(prop, "Voxel Data", "The voxel data associated with this texture"); + RNA_def_property_update(prop, NC_TEXTURE, NULL); +} + static void rna_def_texture(BlenderRNA *brna) { StructRNA *srna; @@ -1324,6 +1539,8 @@ static void rna_def_texture(BlenderRNA *brna) {TEX_MUSGRAVE, "MUSGRAVE", ICON_TEXTURE, "Musgrave", ""}, {TEX_VORONOI, "VORONOI", ICON_TEXTURE, "Voronoi", ""}, {TEX_DISTNOISE, "DISTORTED_NOISE", ICON_TEXTURE, "Distorted Noise", ""}, + {TEX_POINTDENSITY, "POINT_DENSITY", ICON_TEXTURE, "Point Density", ""}, + {TEX_VOXELDATA, "VOXEL_DATA", ICON_TEXTURE, "Voxel Data", ""}, {0, NULL, 0, NULL, NULL}}; srna= RNA_def_struct(brna, "Texture", "ID"); @@ -1336,6 +1553,7 @@ static void rna_def_texture(BlenderRNA *brna) //RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_Texture_type_set", NULL); RNA_def_property_ui_text(prop, "Type", ""); RNA_def_property_update(prop, NC_TEXTURE, NULL); @@ -1399,6 +1617,8 @@ static void rna_def_texture(BlenderRNA *brna) rna_def_texture_musgrave(brna); rna_def_texture_voronoi(brna); rna_def_texture_distorted_noise(brna); + rna_def_texture_pointdensity(brna); + rna_def_texture_voxeldata(brna); /* XXX add more types here .. */ } diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index 185662c5a42..ad96b9db166 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -30,6 +30,7 @@ SET(INC intern/include ../../../intern/guardedalloc ../blenlib ../makesdna extern/include ../blenkernel ../imbuf ../include ../../kernel/gen_messaging ../blenloader + ../../../intern/smoke/extern ../makesrna ) diff --git a/source/blender/render/SConscript b/source/blender/render/SConscript index dbd9f65254b..db151775b96 100644 --- a/source/blender/render/SConscript +++ b/source/blender/render/SConscript @@ -6,7 +6,7 @@ sources = env.Glob('intern/source/*.c') incs = 'intern/include #/intern/guardedalloc ../blenlib ../makesdna ../makesrna' incs += ' extern/include ../blenkernel ../radiosity/extern/include ../imbuf' -incs += ' ../include ../blenloader' +incs += ' ../include ../blenloader ../../../intern/smoke/extern' defs = [] diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index 5f5b493a9ec..435e3ddc07f 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -112,7 +112,7 @@ typedef struct ShadeInput /* internal face coordinates */ float u, v, dx_u, dx_v, dy_u, dy_v; - float co[3], view[3]; + float co[3], view[3], camera_co[3]; /* copy from material, keep synced so we can do memcopy */ /* current size: 23*4 */ @@ -160,6 +160,7 @@ typedef struct ShadeInput int samplenr; /* sample counter, to detect if we should do shadow again */ int depth; /* 1 or larger on raytrace shading */ + int volume_depth; /* number of intersections through volumes */ /* stored copy of original face normal (facenor) * before flipping. Used in Front/back output on geometry node */ diff --git a/source/blender/render/intern/include/pointdensity.h b/source/blender/render/intern/include/pointdensity.h new file mode 100644 index 00000000000..93cdef3b14e --- /dev/null +++ b/source/blender/render/intern/include/pointdensity.h @@ -0,0 +1,44 @@ +/* + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Matt Ebb + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef POINTDENSITY_H +#define POINTDENSITY_H + +/** + * Make point density kd-trees for all point density textures in the scene + */ + +struct Render; +struct TexResult; + +void make_pointdensities(struct Render *re); +void free_pointdensities(struct Render *re); +int pointdensitytex(struct Tex *tex, float *texvec, struct TexResult *texres); + +#endif /* POINTDENSITY_H */ + diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index ab3758781ce..c4910f7733d 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -201,6 +201,9 @@ struct Render ListBase customdata_names; struct Object *excludeob; + ListBase render_volumes_inside; + ListBase volumes; + ListBase volume_precache_parts; /* arena for allocating data for use during render, for * example dynamic TFaces to go in the VlakRen structure. @@ -292,7 +295,9 @@ typedef struct ObjectInstanceRen { float dupliorco[3], dupliuv[2]; float (*duplitexmat)[4]; - + + struct VolumePrecache *volume_precache; + float *vectors; int totvector; } ObjectInstanceRen; @@ -402,6 +407,45 @@ typedef struct StrandRen { float orco[3]; } StrandRen; +/* ------------------------------------------------------------------------- */ + +typedef struct VolumeOb +{ + struct VolumeOb *next, *prev; + struct Material *ma; + struct ObjectRen *obr; +} VolumeOb; + +typedef struct MatInside { + struct MatInside *next, *prev; + struct Material *ma; +} MatInside; + +typedef struct VolPrecachePart +{ + struct VolPrecachePart *next, *prev; + struct RayTree *tree; + struct ShadeInput *shi; + struct ObjectInstanceRen *obi; + int num; + int minx, maxx; + int miny, maxy; + int minz, maxz; + int res[3]; + float bbmin[3]; + float voxel[3]; + int working, done; +} VolPrecachePart; + +typedef struct VolumePrecache +{ + int res[3]; + float *data_r; + float *data_g; + float *data_b; +} VolumePrecache; + +/* ------------------------------------------------------------------------- */ struct LampRen; struct MTex; diff --git a/source/blender/render/intern/include/shading.h b/source/blender/render/intern/include/shading.h index d195f32d5ef..95bccd2be1e 100644 --- a/source/blender/render/intern/include/shading.h +++ b/source/blender/render/intern/include/shading.h @@ -33,6 +33,7 @@ struct VlakRen; struct StrandSegment; struct StrandPoint; struct ObjectInstanceRen obi; +struct Isect; /* shadeinput.c */ @@ -52,6 +53,7 @@ typedef struct ShadeSample { /* also the node shader callback */ void shade_material_loop(struct ShadeInput *shi, struct ShadeResult *shr); +void shade_volume_loop(struct ShadeInput *shi, struct ShadeResult *shr); void shade_input_set_triangle_i(struct ShadeInput *shi, struct ObjectInstanceRen *obi, struct VlakRen *vlr, short i1, short i2, short i3); void shade_input_set_triangle(struct ShadeInput *shi, volatile int obi, volatile int facenr, int normal_flip); @@ -87,7 +89,11 @@ void shade_color(struct ShadeInput *shi, ShadeResult *shr); void ambient_occlusion_to_diffuse(struct ShadeInput *shi, float *diff); void ambient_occlusion(struct ShadeInput *shi); +ListBase *get_lights(struct ShadeInput *shi); float lamp_get_visibility(struct LampRen *lar, float *co, float *lv, float *dist); void lamp_get_shadow(struct LampRen *lar, ShadeInput *shi, float inp, float *shadfac, int do_real); float fresnel_fac(float *view, float *vn, float fresnel, float fac); + +/* rayshade.c */ +extern void shade_ray(struct Isect *is, struct ShadeInput *shi, struct ShadeResult *shr); diff --git a/source/blender/render/intern/include/texture.h b/source/blender/render/intern/include/texture.h index c254b768292..78d6a912af1 100644 --- a/source/blender/render/intern/include/texture.h +++ b/source/blender/render/intern/include/texture.h @@ -56,6 +56,7 @@ void do_halo_tex(struct HaloRen *har, float xn, float yn, float *colf); void do_sky_tex(float *rco, float *lo, float *dxyview, float *hor, float *zen, float *blend, int skyflag, short thread); void do_material_tex(struct ShadeInput *shi); void do_lamp_tex(LampRen *la, float *lavec, struct ShadeInput *shi, float *colf, int effect); +void do_volume_tex(struct ShadeInput *shi, float *xyz, int mapto_flag, float *col, float *val); void init_render_textures(Render *re); void end_render_textures(void); diff --git a/source/blender/render/intern/include/volume_precache.h b/source/blender/render/intern/include/volume_precache.h new file mode 100644 index 00000000000..d8a94c2d560 --- /dev/null +++ b/source/blender/render/intern/include/volume_precache.h @@ -0,0 +1,34 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Matt Ebb. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +void volume_precache(Render *re); +void free_volume_precache(Render *re); +int point_inside_volume_objectinstance(ObjectInstanceRen *obi, float *co); +int using_lightcache(Material *ma); + +#define VOL_MS_TIMESTEP 0.1f \ No newline at end of file diff --git a/source/blender/render/intern/include/volumetric.h b/source/blender/render/intern/include/volumetric.h new file mode 100644 index 00000000000..543e179bc88 --- /dev/null +++ b/source/blender/render/intern/include/volumetric.h @@ -0,0 +1,47 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Matt Ebb. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +float vol_get_stepsize(struct ShadeInput *shi, int context); +float vol_get_density(struct ShadeInput *shi, float *co); +void vol_get_scattering(ShadeInput *shi, float *scatter_col, float *co, float stepsize, float density); + +void shade_volume_outside(ShadeInput *shi, ShadeResult *shr); +void shade_volume_inside(ShadeInput *shi, ShadeResult *shr); +void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is); + +#define STEPSIZE_VIEW 0 +#define STEPSIZE_SHADE 1 + +#define VOL_IS_BACKFACE 1 +#define VOL_IS_SAMEMATERIAL 2 + +#define VOL_BOUNDS_DEPTH 0 +#define VOL_BOUNDS_SS 1 + +#define VOL_SHADE_OUTSIDE 0 +#define VOL_SHADE_INSIDE 1 \ No newline at end of file diff --git a/source/blender/render/intern/include/voxeldata.h b/source/blender/render/intern/include/voxeldata.h new file mode 100644 index 00000000000..b291bdc096d --- /dev/null +++ b/source/blender/render/intern/include/voxeldata.h @@ -0,0 +1,45 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Raul Fernandez Hernandez (Farsthary), Matt Ebb. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef VOXELDATA_H +#define VOXELDATA_H + +struct Render; +struct TexResult; + +typedef struct VoxelDataHeader +{ + int resolX, resolY, resolZ; + int frames; +} VoxelDataHeader; + +void make_voxeldata(struct Render *re); +void free_voxeldata(struct Render *re); +int voxeldatatex(struct Tex *tex, float *texvec, struct TexResult *texres); + +#endif /* VOXELDATA_H */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 2c264ce2337..e486daf2585 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -100,6 +100,8 @@ #include "envmap.h" #include "occlusion.h" +#include "pointdensity.h" +#include "voxeldata.h" #include "render_types.h" #include "rendercore.h" #include "renderdatabase.h" @@ -108,6 +110,7 @@ #include "shading.h" #include "strand.h" #include "texture.h" +#include "volume_precache.h" #include "sss.h" #include "strand.h" #include "zbuf.h" @@ -917,6 +920,7 @@ static Material *give_render_material(Render *re, Object *ob, int nr) if(re->r.mode & R_SPEED) ma->texco |= NEED_UV; + if(ma->material_type == MA_TYPE_VOLUME) ma->mode |= MA_TRANSP; if((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP)) re->flag |= R_ZTRA; @@ -2976,6 +2980,52 @@ static void use_mesh_edge_lookup(ObjectRen *obr, DerivedMesh *dm, MEdge *medge, } } +static void free_camera_inside_volumes(Render *re) +{ + BLI_freelistN(&re->render_volumes_inside); +} + +static void init_camera_inside_volumes(Render *re) +{ + ObjectInstanceRen *obi; + VolumeOb *vo; + float co[3] = {0.f, 0.f, 0.f}; + + for(vo= re->volumes.first; vo; vo= vo->next) { + for(obi= re->instancetable.first; obi; obi= obi->next) { + if (obi->obr == vo->obr) { + if (point_inside_volume_objectinstance(obi, co)) { + MatInside *mi; + + mi = MEM_mallocN(sizeof(MatInside), "camera inside material"); + mi->ma = vo->ma; + + BLI_addtail(&(re->render_volumes_inside), mi); + } + } + } + } + + /* debug { + MatInside *m; + for (m=re->render_volumes_inside.first; m; m=m->next) { + printf("matinside: ma: %s \n", m->ma->id.name+2); + } + }*/ +} + +static void add_volume(Render *re, ObjectRen *obr, Material *ma) +{ + struct VolumeOb *vo; + + vo = MEM_mallocN(sizeof(VolumeOb), "volume object"); + + vo->ma = ma; + vo->obr = obr; + + BLI_addtail(&re->volumes, vo); +} + static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) { Object *ob= obr->ob; @@ -3025,6 +3075,9 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) } need_nmap_tangent= 1; } + + if (ma->material_type == MA_TYPE_VOLUME) + add_volume(re, obr, ma); } } @@ -3809,6 +3862,7 @@ static void set_fullsample_flag(Render *re, ObjectRen *obr) vlr->flag |= R_FULL_OSA; else if(trace) { if(mode & MA_SHLESS); + else if(vlr->mat->material_type == MA_TYPE_VOLUME); else if((mode & MA_RAYMIRROR) || ((mode & MA_TRANSP) && (mode & MA_RAYTRANSP))) /* for blurry reflect/refract, better to take more samples * inside the raytrace than as OSA samples */ @@ -4334,6 +4388,8 @@ void RE_Database_Free(Render *re) curvemapping_free(lar->curfalloff); } + free_volume_precache(re); + BLI_freelistN(&re->lampren); BLI_freelistN(&re->lights); @@ -4360,6 +4416,11 @@ void RE_Database_Free(Render *re) end_render_materials(); end_render_textures(); + free_pointdensities(re); + free_voxeldata(re); + + free_camera_inside_volumes(re); + if(re->wrld.aosphere) { MEM_freeN(re->wrld.aosphere); re->wrld.aosphere= NULL; @@ -4752,6 +4813,8 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) /* MAKE RENDER DATA */ database_init_objects(re, lay, 0, 0, 0, 0); + + init_camera_inside_volumes(re); if(!re->test_break(re->tbh)) { int tothalo; @@ -4800,6 +4863,13 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) /* ENVIRONMENT MAPS */ if(!re->test_break(re->tbh)) make_envmaps(re); + + /* point density texture */ + if(!re->test_break(re->tbh)) + make_pointdensities(re); + /* voxel data texture */ + if(!re->test_break(re->tbh)) + make_voxeldata(re); } if(!re->test_break(re->tbh)) @@ -4816,6 +4886,11 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) if((re->r.mode & R_SSS) && !re->test_break(re->tbh)) if(re->r.renderer==R_INTERN) make_sss_tree(re); + + if(!re->test_break(re->tbh)) + if(re->r.mode & R_RAYTRACE) + volume_precache(re); + } if(re->test_break(re->tbh)) diff --git a/source/blender/render/intern/source/occlusion.c b/source/blender/render/intern/source/occlusion.c index feef3dd424a..a15377a8c6d 100644 --- a/source/blender/render/intern/source/occlusion.c +++ b/source/blender/render/intern/source/occlusion.c @@ -630,7 +630,7 @@ static OcclusionTree *occ_tree_build(Render *re) if((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; else vlr++; - if(vlr->mat->mode & MA_TRACEBLE) + if((vlr->mat->mode & MA_TRACEBLE) && (vlr->mat->material_type == MA_TYPE_SURFACE)) totface++; } } @@ -663,7 +663,7 @@ static OcclusionTree *occ_tree_build(Render *re) if((a & 255)==0) vlr= obr->vlaknodes[a>>8].vlak; else vlr++; - if(vlr->mat->mode & MA_TRACEBLE) { + if((vlr->mat->mode & MA_TRACEBLE) && (vlr->mat->material_type == MA_TYPE_SURFACE)) { tree->face[b].obi= c; tree->face[b].facenr= a; tree->occlusion[b]= 1.0f; diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c new file mode 100644 index 00000000000..fbc39b61257 --- /dev/null +++ b/source/blender/render/intern/source/pointdensity.c @@ -0,0 +1,484 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributors: Matt Ebb + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_kdopbvh.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_global.h" +#include "BKE_lattice.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_texture.h" + +#include "DNA_texture_types.h" +#include "DNA_particle_types.h" + +#include "render_types.h" +#include "renderdatabase.h" +#include "texture.h" + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ +/* only to be used here in this file, it's for speed */ +extern struct Render R; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + +static int point_data_used(PointDensity *pd) +{ + int pd_bitflag = 0; + + if ((pd->noise_influence == TEX_PD_NOISE_VEL) || (pd->color_source == TEX_PD_COLOR_PARTVEL) || (pd->color_source == TEX_PD_COLOR_PARTSPEED)) + pd_bitflag |= POINT_DATA_VEL; + if ((pd->noise_influence == TEX_PD_NOISE_AGE) || (pd->color_source == TEX_PD_COLOR_PARTAGE)) + pd_bitflag |= POINT_DATA_LIFE; + + return pd_bitflag; +} + + +/* additional data stored alongside the point density BVH, + * accessible by point index number to retrieve other information + * such as particle velocity or lifetime */ +static void alloc_point_data(PointDensity *pd, int total_particles, int point_data_used) +{ + int data_size = 0; + + if (point_data_used & POINT_DATA_VEL) { + /* store 3 channels of velocity data */ + data_size += 3; + } + if (point_data_used & POINT_DATA_LIFE) { + /* store 1 channel of lifetime data */ + data_size += 1; + } + + if (data_size) + pd->point_data = MEM_mallocN(sizeof(float)*data_size*total_particles, "particle point data"); +} + +static void pointdensity_cache_psys(Render *re, PointDensity *pd, Object *ob, ParticleSystem *psys) +{ + DerivedMesh* dm; + ParticleKey state; + ParticleData *pa=NULL; + float cfra = bsystem_time(re->scene, ob, (float)re->scene->r.cfra, 0.0); + int i, childexists; + int total_particles, offset=0; + int data_used = point_data_used(pd); + float partco[3]; + float obview[4][4]; + + + /* init everything */ + if (!psys || !ob || !pd) return; + + Mat4MulMat4(obview, re->viewinv, ob->obmat); + + /* Just to create a valid rendering context for particles */ + psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, 0); + + dm = mesh_create_derived_render(re->scene, ob,CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL); + + if ( !psys_check_enabled(ob, psys)) { + psys_render_restore(ob, psys); + return; + } + + /* in case ob->imat isn't up-to-date */ + Mat4Invert(ob->imat, ob->obmat); + + total_particles = psys->totpart+psys->totchild; + psys->lattice=psys_get_lattice(re->scene,ob,psys); + + pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6); + alloc_point_data(pd, total_particles, data_used); + pd->totpoints = total_particles; + if (data_used & POINT_DATA_VEL) offset = pd->totpoints*3; + + if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) + childexists = 1; + + for (i=0, pa=psys->particles; i < total_particles; i++, pa++) { + + state.time = cfra; + if(psys_get_particle_state(re->scene, ob, psys, i, &state, 0)) { + + VECCOPY(partco, state.co); + + if (pd->psys_cache_space == TEX_PD_OBJECTSPACE) + Mat4MulVecfl(ob->imat, partco); + else if (pd->psys_cache_space == TEX_PD_OBJECTLOC) { + float obloc[3]; + VECCOPY(obloc, ob->loc); + VecSubf(partco, partco, obloc); + } else { + /* TEX_PD_WORLDSPACE */ + } + + BLI_bvhtree_insert(pd->point_tree, i, partco, 1); + + if (data_used & POINT_DATA_VEL) { + pd->point_data[i*3 + 0] = state.vel[0]; + pd->point_data[i*3 + 1] = state.vel[1]; + pd->point_data[i*3 + 2] = state.vel[2]; + } + if (data_used & POINT_DATA_LIFE) { + float pa_time; + + if (i < psys->totpart) { + pa_time = (cfra - pa->time)/pa->lifetime; + } else { + ChildParticle *cpa= (psys->child + i) - psys->totpart; + float pa_birthtime, pa_dietime; + + pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime); + } + + pd->point_data[offset + i] = pa_time; + } + } + } + + BLI_bvhtree_balance(pd->point_tree); + dm->release(dm); + + if(psys->lattice){ + end_latt_deform(psys->lattice); + psys->lattice=0; + } + + psys_render_restore(ob, psys); +} + + +static void pointdensity_cache_object(Render *re, PointDensity *pd, ObjectRen *obr) +{ + int i; + + if (!obr || !pd) return; + if(!obr->vertnodes) return; + + /* in case ob->imat isn't up-to-date */ + Mat4Invert(obr->ob->imat, obr->ob->obmat); + + pd->point_tree = BLI_bvhtree_new(obr->totvert, 0.0, 4, 6); + pd->totpoints = obr->totvert; + + for(i=0; itotvert; i++) { + float ver_co[3]; + VertRen *ver= RE_findOrAddVert(obr, i); + + VECCOPY(ver_co, ver->co); + Mat4MulVecfl(re->viewinv, ver_co); + + if (pd->ob_cache_space == TEX_PD_OBJECTSPACE) { + Mat4MulVecfl(obr->ob->imat, ver_co); + } else if (pd->psys_cache_space == TEX_PD_OBJECTLOC) { + VecSubf(ver_co, ver_co, obr->ob->loc); + } else { + /* TEX_PD_WORLDSPACE */ + } + + BLI_bvhtree_insert(pd->point_tree, i, ver_co, 1); + } + + BLI_bvhtree_balance(pd->point_tree); + +} +static void cache_pointdensity(Render *re, Tex *tex) +{ + PointDensity *pd = tex->pd; + + if (pd->point_tree) { + BLI_bvhtree_free(pd->point_tree); + pd->point_tree = NULL; + } + + if (pd->source == TEX_PD_PSYS) { + Object *ob = pd->object; + + if (!ob) return; + if (!pd->psys) return; + + pointdensity_cache_psys(re, pd, ob, pd->psys); + } + else if (pd->source == TEX_PD_OBJECT) { + Object *ob = pd->object; + ObjectRen *obr; + int found=0; + + /* find the obren that corresponds to the object */ + for (obr=re->objecttable.first; obr; obr=obr->next) { + if (obr->ob == ob) { + found=1; + break; + } + } + if (!found) return; + + pointdensity_cache_object(re, pd, obr); + } +} + +static void free_pointdensity(Render *re, Tex *tex) +{ + PointDensity *pd = tex->pd; + + if (!pd) return; + + if (pd->point_tree) { + BLI_bvhtree_free(pd->point_tree); + pd->point_tree = NULL; + } + + if (pd->point_data) { + MEM_freeN(pd->point_data); + pd->point_data = NULL; + } + pd->totpoints = 0; +} + + + +void make_pointdensities(Render *re) +{ + Tex *tex; + + if(re->scene->r.scemode & R_PREVIEWBUTS) + return; + + re->i.infostr= "Caching Point Densities"; + re->stats_draw(re->sdh, &re->i); + + for (tex= G.main->tex.first; tex; tex= tex->id.next) { + if(tex->id.us && tex->type==TEX_POINTDENSITY) { + cache_pointdensity(re, tex); + } + } + + re->i.infostr= NULL; + re->stats_draw(re->sdh, &re->i); +} + +void free_pointdensities(Render *re) +{ + Tex *tex; + + if(re->scene->r.scemode & R_PREVIEWBUTS) + return; + + for (tex= G.main->tex.first; tex; tex= tex->id.next) { + if(tex->id.us && tex->type==TEX_POINTDENSITY) { + free_pointdensity(re, tex); + } + } +} + +typedef struct PointDensityRangeData +{ + float *density; + float squared_radius; + float *point_data; + float *vec; + float softness; + short falloff_type; + short noise_influence; + float *age; + int point_data_used; + int offset; +} PointDensityRangeData; + +void accum_density(void *userdata, int index, float squared_dist) +{ + PointDensityRangeData *pdr = (PointDensityRangeData *)userdata; + const float dist = (pdr->squared_radius - squared_dist) / pdr->squared_radius * 0.5f; + float density; + + if (pdr->falloff_type == TEX_PD_FALLOFF_STD) + density = dist; + else if (pdr->falloff_type == TEX_PD_FALLOFF_SMOOTH) + density = 3.0f*dist*dist - 2.0f*dist*dist*dist; + else if (pdr->falloff_type == TEX_PD_FALLOFF_SOFT) + density = pow(dist, pdr->softness); + else if (pdr->falloff_type == TEX_PD_FALLOFF_CONSTANT) + density = pdr->squared_radius; + else if (pdr->falloff_type == TEX_PD_FALLOFF_ROOT) + density = sqrt(dist); + + if (pdr->point_data_used & POINT_DATA_VEL) { + pdr->vec[0] += pdr->point_data[index*3 + 0]; //* density; + pdr->vec[1] += pdr->point_data[index*3 + 1]; //* density; + pdr->vec[2] += pdr->point_data[index*3 + 2]; //* density; + } + if (pdr->point_data_used & POINT_DATA_LIFE) { + *pdr->age += pdr->point_data[pdr->offset + index]; // * density; + } + + *pdr->density += density; +} + + +static void init_pointdensityrangedata(PointDensity *pd, PointDensityRangeData *pdr, float *density, float *vec, float *age) +{ + pdr->squared_radius = pd->radius*pd->radius; + pdr->density = density; + pdr->point_data = pd->point_data; + pdr->falloff_type = pd->falloff_type; + pdr->vec = vec; + pdr->age = age; + pdr->softness = pd->falloff_softness; + pdr->noise_influence = pd->noise_influence; + pdr->point_data_used = point_data_used(pd); + pdr->offset = (pdr->point_data_used & POINT_DATA_VEL)?pd->totpoints*3:0; +} + + +int pointdensitytex(Tex *tex, float *texvec, TexResult *texres) +{ + int retval = TEX_INT; + PointDensity *pd = tex->pd; + PointDensityRangeData pdr; + float density=0.0f, age=0.0f, time=0.0f; + float vec[3] = {0.0f, 0.0f, 0.0f}, co[3]; + float col[4]; + float turb, noise_fac; + int num=0; + + texres->tin = 0.0f; + + if ((!pd) || (!pd->point_tree)) + return 0; + + init_pointdensityrangedata(pd, &pdr, &density, vec, &age); + noise_fac = pd->noise_fac * 0.5f; /* better default */ + + VECCOPY(co, texvec); + + if (point_data_used(pd)) { + /* does a BVH lookup to find accumulated density and additional point data * + * stores particle velocity vector in 'vec', and particle lifetime in 'time' */ + num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr); + if (num > 0) { + age /= num; + VecMulf(vec, 1.0f/num); + } + + /* reset */ + density = vec[0] = vec[1] = vec[2] = 0.0f; + } + + if (pd->flag & TEX_PD_TURBULENCE) { + + if (pd->noise_influence == TEX_PD_NOISE_AGE) { + turb = BLI_gTurbulence(pd->noise_size, texvec[0]+age, texvec[1]+age, texvec[2]+age, pd->noise_depth, 0, pd->noise_basis); + } + else if (pd->noise_influence == TEX_PD_NOISE_TIME) { + time = R.cfra / (float)R.r.efra; + turb = BLI_gTurbulence(pd->noise_size, texvec[0]+time, texvec[1]+time, texvec[2]+time, pd->noise_depth, 0, pd->noise_basis); + //turb = BLI_turbulence(pd->noise_size, texvec[0]+time, texvec[1]+time, texvec[2]+time, pd->noise_depth); + } + else { + turb = BLI_gTurbulence(pd->noise_size, texvec[0]+vec[0], texvec[1]+vec[1], texvec[2]+vec[2], pd->noise_depth, 0, pd->noise_basis); + } + + turb -= 0.5f; /* re-center 0.0-1.0 range around 0 to prevent offsetting result */ + + /* now we have an offset coordinate to use for the density lookup */ + co[0] = texvec[0] + noise_fac * turb; + co[1] = texvec[1] + noise_fac * turb; + co[2] = texvec[2] + noise_fac * turb; + } + + /* BVH query with the potentially perturbed coordinates */ + num = BLI_bvhtree_range_query(pd->point_tree, co, pd->radius, accum_density, &pdr); + if (num > 0) { + age /= num; + VecMulf(vec, 1.0f/num); + } + + texres->tin = density; + BRICONT; + + if (pd->color_source == TEX_PD_COLOR_CONSTANT) + return retval; + + retval |= TEX_RGB; + + switch (pd->color_source) { + case TEX_PD_COLOR_PARTAGE: + if (pd->coba) { + if (do_colorband(pd->coba, age, col)) { + texres->talpha= 1; + VECCOPY(&texres->tr, col); + texres->tin *= col[3]; + texres->ta = texres->tin; + } + } + break; + case TEX_PD_COLOR_PARTSPEED: + { + float speed = VecLength(vec) * pd->speed_scale; + + if (pd->coba) { + if (do_colorband(pd->coba, speed, col)) { + texres->talpha= 1; + VECCOPY(&texres->tr, col); + texres->tin *= col[3]; + texres->ta = texres->tin; + } + } + break; + } + case TEX_PD_COLOR_PARTVEL: + texres->talpha= 1; + VecMulf(vec, pd->speed_scale); + VECCOPY(&texres->tr, vec); + texres->ta = texres->tin; + break; + case TEX_PD_COLOR_CONSTANT: + default: + texres->tr = texres->tg = texres->tb = texres->ta = 1.0f; + break; + } + BRICONTRGB; + + return retval; + + /* + if (texres->nor!=NULL) { + texres->nor[0] = texres->nor[1] = texres->nor[2] = 0.0f; + } + */ +} diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c index 0c8749ce329..4cc731611a4 100644 --- a/source/blender/render/intern/source/rayshade.c +++ b/source/blender/render/intern/source/rayshade.c @@ -54,6 +54,7 @@ #include "pixelshading.h" #include "shading.h" #include "texture.h" +#include "volumetric.h" #include "RE_raytrace.h" @@ -95,6 +96,17 @@ static int vlr_check_intersect(Isect *is, int ob, RayFace *face) return (is->lay & obi->lay); } +static int vlr_check_intersect_solid(Isect *is, int ob, RayFace *face) +{ + VlakRen *vlr = (VlakRen*)face; + + /* solid material types only */ + if (vlr->mat->material_type == MA_TYPE_SURFACE) + return 1; + else + return 0; +} + static float *vlr_get_transform(void *userdata, int i) { ObjectInstanceRen *obi= RAY_OBJECT_GET((Render*)userdata, i); @@ -205,7 +217,7 @@ void makeraytree(Render *re) re->stats_draw(re->sdh, &re->i); } -static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) +void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) { VlakRen *vlr= (VlakRen*)is->face; ObjectInstanceRen *obi= RAY_OBJECT_GET(&R, is->ob); @@ -260,8 +272,14 @@ static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) shade_input_flip_normals(shi); shade_input_set_shade_texco(shi); - - if(is->mode==RE_RAY_SHADOW_TRA) { + if (shi->mat->material_type == MA_TYPE_VOLUME) { + if(ELEM(is->mode, RE_RAY_SHADOW, RE_RAY_SHADOW_TRA)) { + shade_volume_shadow(shi, shr, is); + } else { + shade_volume_outside(shi, shr); + } + } + else if(is->mode==RE_RAY_SHADOW_TRA) { /* temp hack to prevent recursion */ if(shi->nodes==0 && shi->mat->nodetree && shi->mat->use_nodes) { ntreeShaderExecTree(shi->mat->nodetree, shi, shr); @@ -275,9 +293,20 @@ static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) ntreeShaderExecTree(shi->mat->nodetree, shi, shr); shi->mat= vlr->mat; /* shi->mat is being set in nodetree */ } - else - shade_material_loop(shi, shr); - + else { + int tempdepth; + /* XXX dodgy business here, set ray depth to -1 + * to ignore raytrace in shade_material_loop() + * this could really use a refactor --Matt */ + if (shi->volume_depth == 0) { + tempdepth = shi->depth; + shi->depth = -1; + shade_material_loop(shi, shr); + shi->depth = tempdepth; + } else { + shade_material_loop(shi, shr); + } + } /* raytrace likes to separate the spec color */ VECSUB(shr->diff, shr->combined, shr->spec); } @@ -1302,11 +1331,15 @@ static void ray_trace_shadow_tra(Isect *is, ShadeInput *origshi, int depth, int shi.nodes= origshi->nodes; shade_ray(is, &shi, &shr); - if (traflag & RAY_TRA) - d= shade_by_transmission(is, &shi, &shr); - - /* mix colors based on shadfac (rgb + amount of light factor) */ - addAlphaLight(is->col, shr.diff, shr.alpha, d*shi.mat->filter); + if (shi.mat->material_type == MA_TYPE_SURFACE) { + if (traflag & RAY_TRA) + d= shade_by_transmission(is, &shi, &shr); + + /* mix colors based on shadfac (rgb + amount of light factor) */ + addAlphaLight(is->col, shr.diff, shr.alpha, d*shi.mat->filter); + } else if (shi.mat->material_type == MA_TYPE_VOLUME) { + addAlphaLight(is->col, shr.combined, shr.alpha, 1.0f); + } if(depth>0 && is->col[3]>0.0f) { @@ -1607,7 +1640,7 @@ static void ray_ao_qmc(ShadeInput *shi, float *shadfac) prev = fac; - if(RE_ray_tree_intersect(R.raytree, &isec)) { + if(RE_ray_tree_intersect_check(R.raytree, &isec, vlr_check_intersect_solid)) { if (R.wrld.aomode & WO_AODIST) fac+= exp(-isec.labda*R.wrld.aodistfac); else fac+= 1.0f; } @@ -1732,7 +1765,7 @@ static void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) isec.end[2] = shi->co[2] - maxdist*vec[2]; /* do the trace */ - if(RE_ray_tree_intersect(R.raytree, &isec)) { + if(RE_ray_tree_intersect_check(R.raytree, &isec, vlr_check_intersect_solid)) { if (R.wrld.aomode & WO_AODIST) sh+= exp(-isec.labda*R.wrld.aodistfac); else sh+= 1.0f; } diff --git a/source/blender/render/intern/source/raytrace.c b/source/blender/render/intern/source/raytrace.c index 09d3711885a..b34fe6a7039 100644 --- a/source/blender/render/intern/source/raytrace.c +++ b/source/blender/render/intern/source/raytrace.c @@ -931,7 +931,7 @@ int RE_ray_face_intersection(Isect *is, RayObjectTransformFunc transformfunc, Ra intersection to be detected in its neighbour face */ if(is->facecontr && is->faceisect); // optimizing, the tests below are not needed - else if(is->labda< .1) { + else if(is->labda< .1 && is->faceorig) { RayFace *face= is->faceorig; float *origv1, *origv2, *origv3, *origv4; short de= 0; diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index 857b401e298..1a0edfd53ed 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -52,6 +52,7 @@ #include "shading.h" #include "strand.h" #include "texture.h" +#include "volumetric.h" #include "zbuf.h" /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -166,6 +167,11 @@ void shade_material_loop(ShadeInput *shi, ShadeResult *shr) if((shi->layflag & SCE_LAY_SKY) && (R.r.alphamode==R_ADDSKY)) shr->alpha= 1.0f; } + + if(R.r.mode & R_RAYTRACE) { + if (R.render_volumes_inside.first) + shade_volume_inside(shi, shr); + } } @@ -183,7 +189,12 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr) /* copy all relevant material vars, note, keep this synced with render_types.h */ shade_input_init_material(shi); - shade_material_loop(shi, shr); + if (shi->mat->material_type == MA_TYPE_VOLUME) { + if(R.r.mode & R_RAYTRACE) + shade_volume_outside(shi, shr); + } else { /* MA_TYPE_SURFACE, MA_TYPE_WIRE */ + shade_material_loop(shi, shr); + } } /* copy additional passes */ @@ -210,11 +221,12 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr) if(shr->alpha!=1.0f || alpha!=1.0f) { float fac= alpha*(shr->alpha); shr->combined[3]= fac; - shr->combined[0]*= fac; - shr->combined[1]*= fac; - shr->combined[2]*= fac; + + if (shi->mat->material_type!= MA_TYPE_VOLUME) + VecMulf(shr->combined, fac); } - else shr->combined[3]= 1.0f; + else + shr->combined[3]= 1.0f; /* add z */ shr->z= -shi->co[2]; @@ -698,6 +710,10 @@ void shade_input_calc_viewco(ShadeInput *shi, float x, float y, float z, float * } } + /* set camera coords - for scanline, it's always 0.0,0.0,0.0 (render is in camera space) + * however for raytrace it can be different - the position of the last intersection */ + shi->camera_co[0] = shi->camera_co[1] = shi->camera_co[2] = 0.0f; + /* cannot normalize earlier, code above needs it at viewplane level */ Normalize(view); } diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c index d5c8cf30b30..2f6f86e9a09 100644 --- a/source/blender/render/intern/source/shadeoutput.c +++ b/source/blender/render/intern/source/shadeoutput.c @@ -58,7 +58,7 @@ extern struct Render R; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -static ListBase *get_lights(ShadeInput *shi) +ListBase *get_lights(ShadeInput *shi) { if(R.r.scemode & R_PREVIEWBUTS) diff --git a/source/blender/render/intern/source/texture.c b/source/blender/render/intern/source/texture.c index e6d6e78e27b..c706c6ccc11 100644 --- a/source/blender/render/intern/source/texture.c +++ b/source/blender/render/intern/source/texture.c @@ -65,6 +65,8 @@ #include "BKE_ipo.h" #include "envmap.h" +#include "pointdensity.h" +#include "voxeldata.h" #include "renderpipeline.h" #include "render_types.h" #include "rendercore.h" @@ -1262,6 +1264,13 @@ static int multitex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, retval= mg_distNoiseTex(tex, tmpvec, texres); break; + case TEX_POINTDENSITY: + retval= pointdensitytex(tex, texvec, texres); + break; + case TEX_VOXELDATA: + retval= voxeldatatex(tex, texvec, texres); + break; + } if (tex->flag & TEX_COLORBAND) { @@ -1272,7 +1281,7 @@ static int multitex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, texres->tg= col[1]; texres->tb= col[2]; texres->ta= col[3]; - retval |= 1; + retval |= TEX_RGB; } } return retval; @@ -2248,6 +2257,187 @@ void do_material_tex(ShadeInput *shi) } } + +void do_volume_tex(ShadeInput *shi, float *xyz, int mapto_flag, float *col, float *val) +{ + MTex *mtex; + Tex *tex; + TexResult texres= {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL}; + int tex_nr, rgbnor= 0; + float co[3], texvec[3]; + float fact, stencilTin=1.0; + + if (R.r.scemode & R_NO_TEX) return; + /* here: test flag if there's a tex (todo) */ + + for(tex_nr=0; tex_nrmat->septex & (1<mat->mtex[tex_nr]) { + mtex= shi->mat->mtex[tex_nr]; + tex= mtex->tex; + if(tex==0) continue; + + /* only process if this texture is mapped + * to one that we're interested in */ + if (!(mtex->mapto & mapto_flag)) continue; + + /* which coords */ + if(mtex->texco==TEXCO_OBJECT) { + Object *ob= mtex->object; + ob= mtex->object; + if(ob) { + VECCOPY(co, xyz); + if(mtex->texflag & MTEX_OB_DUPLI_ORIG) { + if(shi->obi && shi->obi->duplitexmat) + MTC_Mat4MulVecfl(shi->obi->duplitexmat, co); + } + MTC_Mat4MulVecfl(ob->imat, co); + } + } + /* not really orco, but 'local' */ + else if(mtex->texco==TEXCO_ORCO) { + + if(mtex->texflag & MTEX_DUPLI_MAPTO) { + VECCOPY(co, shi->duplilo); + } + else { + Object *ob= shi->obi->ob; + VECCOPY(co, xyz); + MTC_Mat4MulVecfl(ob->imat, co); + } + } + else if(mtex->texco==TEXCO_GLOB) { + VECCOPY(co, xyz); + MTC_Mat4MulVecfl(R.viewinv, co); + } + else continue; // can happen when texco defines disappear and it renders old files + + texres.nor= NULL; + + if(tex->type==TEX_IMAGE) { + continue; /* not supported yet */ + //do_2d_mapping(mtex, texvec, NULL, NULL, dxt, dyt); + } + else { + /* placement */ + if(mtex->projx) texvec[0]= mtex->size[0]*(co[mtex->projx-1]+mtex->ofs[0]); + else texvec[0]= mtex->size[0]*(mtex->ofs[0]); + + if(mtex->projy) texvec[1]= mtex->size[1]*(co[mtex->projy-1]+mtex->ofs[1]); + else texvec[1]= mtex->size[1]*(mtex->ofs[1]); + + if(mtex->projz) texvec[2]= mtex->size[2]*(co[mtex->projz-1]+mtex->ofs[2]); + else texvec[2]= mtex->size[2]*(mtex->ofs[2]); + } + + rgbnor= multitex(tex, texvec, NULL, NULL, 0, &texres, 0, mtex->which_output); /* NULL = dxt/dyt, 0 = shi->osatex - not supported */ + + /* texture output */ + + if( (rgbnor & TEX_RGB) && (mtex->texflag & MTEX_RGBTOINT)) { + texres.tin= (0.35*texres.tr+0.45*texres.tg+0.2*texres.tb); + rgbnor-= TEX_RGB; + } + if(mtex->texflag & MTEX_NEGATIVE) { + if(rgbnor & TEX_RGB) { + texres.tr= 1.0-texres.tr; + texres.tg= 1.0-texres.tg; + texres.tb= 1.0-texres.tb; + } + texres.tin= 1.0-texres.tin; + } + if(mtex->texflag & MTEX_STENCIL) { + if(rgbnor & TEX_RGB) { + fact= texres.ta; + texres.ta*= stencilTin; + stencilTin*= fact; + } + else { + fact= texres.tin; + texres.tin*= stencilTin; + stencilTin*= fact; + } + } + + + if((mapto_flag & (MAP_EMISSION_COL+MAP_ABSORPTION_COL)) && (mtex->mapto & (MAP_EMISSION_COL+MAP_ABSORPTION_COL))) { + float tcol[3], colfac; + + /* stencil maps on the texture control slider, not texture intensity value */ + colfac= mtex->colfac*stencilTin; + + if((rgbnor & TEX_RGB)==0) { + tcol[0]= mtex->r; + tcol[1]= mtex->g; + tcol[2]= mtex->b; + } else { + tcol[0]=texres.tr; + tcol[1]=texres.tg; + tcol[2]=texres.tb; + if(texres.talpha) + texres.tin= texres.ta; + } + + /* inverse gamma correction */ + if (R.r.color_mgt_flag & R_COLOR_MANAGEMENT) { + color_manage_linearize(tcol, tcol); + } + + /* used for emit */ + if((mapto_flag & MAP_EMISSION_COL) && (mtex->mapto & MAP_EMISSION_COL)) { + texture_rgb_blend(col, tcol, col, texres.tin, colfac, mtex->blendtype); + } + + /* MAP_COLMIR is abused for absorption colour at the moment */ + if((mapto_flag & MAP_ABSORPTION_COL) && (mtex->mapto & MAP_ABSORPTION_COL)) { + texture_rgb_blend(col, tcol, col, texres.tin, colfac, mtex->blendtype); + } + } + + if((mapto_flag & MAP_VARS) && (mtex->mapto & MAP_VARS)) { + /* stencil maps on the texture control slider, not texture intensity value */ + float varfac= mtex->varfac*stencilTin; + + /* convert RGB to intensity if intensity info isn't provided */ + if (!(rgbnor & TEX_INT)) { + if (rgbnor & TEX_RGB) { + if(texres.talpha) texres.tin= texres.ta; + else texres.tin= (0.35*texres.tr+0.45*texres.tg+0.2*texres.tb); + } + } + + if((mapto_flag & MAP_EMISSION) && (mtex->mapto & MAP_EMISSION)) { + int flip= mtex->maptoneg & MAP_EMISSION; + + *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + if(*val<0.0) *val= 0.0; + } + if((mapto_flag & MAP_DENSITY) && (mtex->mapto & MAP_DENSITY)) { + int flip= mtex->maptoneg & MAP_DENSITY; + + *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + CLAMP(*val, 0.0, 1.0); + } + if((mapto_flag & MAP_ABSORPTION) && (mtex->mapto & MAP_ABSORPTION)) { + int flip= mtex->maptoneg & MAP_ABSORPTION; + + *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + CLAMP(*val, 0.0, 1.0); + } + if((mapto_flag & MAP_SCATTERING) && (mtex->mapto & MAP_SCATTERING)) { + int flip= mtex->maptoneg & MAP_SCATTERING; + + *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + CLAMP(*val, 0.0, 1.0); + } + } + } + } +} + + /* ------------------------------------------------------------------------- */ void do_halo_tex(HaloRen *har, float xn, float yn, float *colf) diff --git a/source/blender/render/intern/source/volume_precache.c b/source/blender/render/intern/source/volume_precache.c new file mode 100644 index 00000000000..0b81d775649 --- /dev/null +++ b/source/blender/render/intern/source/volume_precache.c @@ -0,0 +1,736 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Matt Ebb. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_threads.h" +#include "BLI_voxel.h" + +#include "PIL_time.h" + +#include "RE_shader_ext.h" +#include "RE_raytrace.h" + +#include "DNA_material_types.h" + +#include "render_types.h" +#include "renderdatabase.h" +#include "volumetric.h" +#include "volume_precache.h" + +#if defined( _MSC_VER ) && !defined( __cplusplus ) +# define inline __inline +#endif // defined( _MSC_VER ) && !defined( __cplusplus ) + +#include "BKE_global.h" + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ +/* only to be used here in this file, it's for speed */ +extern struct Render R; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* *** utility code to set up an individual raytree for objectinstance, for checking inside/outside *** */ + +/* Recursive test for intersections, from a point inside the mesh, to outside + * Number of intersections (depth) determine if a point is inside or outside the mesh */ +int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int limit, int depth) +{ + if (limit == 0) return depth; + + if (RE_ray_tree_intersect(tree, isect)) { + float hitco[3]; + + hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; + hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; + hitco[2] = isect->start[2] + isect->labda*isect->vec[2]; + VecAddf(isect->start, hitco, offset); + + return intersect_outside_volume(tree, isect, offset, limit-1, depth+1); + } else { + return depth; + } +} + +/* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */ +int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co) +{ + float maxsize = RE_ray_tree_max_size(tree); + Isect isect; + float vec[3] = {0.0f,0.0f,1.0f}; + int final_depth=0, depth=0, limit=20; + + /* set up the isect */ + memset(&isect, 0, sizeof(isect)); + VECCOPY(isect.start, co); + isect.end[0] = co[0] + vec[0] * maxsize; + isect.end[1] = co[1] + vec[1] * maxsize; + isect.end[2] = co[2] + vec[2] * maxsize; + + /* and give it a little offset to prevent self-intersections */ + VecMulf(vec, 1e-5); + VecAddf(isect.start, isect.start, vec); + + isect.mode= RE_RAY_MIRROR; + isect.face_last= NULL; + isect.lay= -1; + + final_depth = intersect_outside_volume(tree, &isect, vec, limit, depth); + + /* even number of intersections: point is outside + * odd number: point is inside */ + if (final_depth % 2 == 0) return 0; + else return 1; +} + +static int inside_check_func(Isect *is, int ob, RayFace *face) +{ + return 1; +} +static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4) +{ + VlakRen *vlr= (VlakRen*)face; + + *v1 = (vlr->v1)? vlr->v1->co: NULL; + *v2 = (vlr->v2)? vlr->v2->co: NULL; + *v3 = (vlr->v3)? vlr->v3->co: NULL; + *v4 = (vlr->v4)? vlr->v4->co: NULL; +} + +RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax) +{ + int v; + VlakRen *vlr= NULL; + + /* create empty raytree */ + RayTree *tree = RE_ray_tree_create(64, obi->obr->totvlak, bbmin, bbmax, + vlr_face_coords, inside_check_func, NULL, NULL); + + /* fill it with faces */ + for(v=0; vobr->totvlak; v++) { + if((v & 255)==0) + vlr= obi->obr->vlaknodes[v>>8].vlak; + else + vlr++; + + RE_ray_tree_add_face(tree, 0, vlr); + } + + RE_ray_tree_done(tree); + + return tree; +} + +/* *** light cache filtering *** */ + +static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz) +{ + int x, y, z, x_, y_, z_; + int added=0; + float tot=0.0f; + + for (z=-1; z <= 1; z++) { + z_ = zz+z; + if (z_ >= 0 && z_ <= res[2]-1) { + + for (y=-1; y <= 1; y++) { + y_ = yy+y; + if (y_ >= 0 && y_ <= res[1]-1) { + + for (x=-1; x <= 1; x++) { + x_ = xx+x; + if (x_ >= 0 && x_ <= res[0]-1) { + + if (cache[ V_I(x_, y_, z_, res) ] > 0.0f) { + tot += cache[ V_I(x_, y_, z_, res) ]; + added++; + } + + } + } + } + } + } + } + + tot /= added; + + return ((added>0)?tot:0.0f); +} + +/* function to filter the edges of the light cache, where there was no volume originally. + * For each voxel which was originally external to the mesh, it finds the average values of + * the surrounding internal voxels and sets the original external voxel to that average amount. + * Works almost a bit like a 'dilate' filter */ +static void lightcache_filter(VolumePrecache *vp) +{ + int x, y, z; + + for (z=0; z < vp->res[2]; z++) { + for (y=0; y < vp->res[1]; y++) { + for (x=0; x < vp->res[0]; x++) { + /* trigger for outside mesh */ + if (vp->data_r[ V_I(x, y, z, vp->res) ] < -0.5f) + vp->data_r[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_r, vp->res, x, y, z); + if (vp->data_g[ V_I(x, y, z, vp->res) ] < -0.5f) + vp->data_g[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_g, vp->res, x, y, z); + if (vp->data_b[ V_I(x, y, z, vp->res) ] < -0.5f) + vp->data_b[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_b, vp->res, x, y, z); + } + } + } +} + +static inline int ms_I(int x, int y, int z, int *n) //has a pad of 1 voxel surrounding the core for boundary simulation +{ + return z*(n[1]+2)*(n[0]+2) + y*(n[0]+2) + x; +} + + +/* *** multiple scattering approximation *** */ + +/* get the total amount of light energy in the light cache. used to normalise after multiple scattering */ +static float total_ss_energy(VolumePrecache *vp) +{ + int x, y, z; + int *res = vp->res; + float energy=0.f; + + for (z=0; z < res[2]; z++) { + for (y=0; y < res[1]; y++) { + for (x=0; x < res[0]; x++) { + if (vp->data_r[ V_I(x, y, z, res) ] > 0.f) energy += vp->data_r[ V_I(x, y, z, res) ]; + if (vp->data_g[ V_I(x, y, z, res) ] > 0.f) energy += vp->data_g[ V_I(x, y, z, res) ]; + if (vp->data_b[ V_I(x, y, z, res) ] > 0.f) energy += vp->data_b[ V_I(x, y, z, res) ]; + } + } + } + + return energy; +} + +static float total_ms_energy(float *sr, float *sg, float *sb, int *res) +{ + int x, y, z, i; + float energy=0.f; + + for (z=1;z<=res[2];z++) { + for (y=1;y<=res[1];y++) { + for (x=1;x<=res[0];x++) { + + i = ms_I(x,y,z,res); + if (sr[i] > 0.f) energy += sr[i]; + if (sg[i] > 0.f) energy += sg[i]; + if (sb[i] > 0.f) energy += sb[i]; + } + } + } + + return energy; +} + +static void ms_diffuse(int b, float* x0, float* x, float diff, int *n) +{ + int i, j, k, l; + const float dt = VOL_MS_TIMESTEP; + const float a = dt*diff*n[0]*n[1]*n[2]; + + for (l=0; l<20; l++) + { + for (k=1; k<=n[2]; k++) + { + for (j=1; j<=n[1]; j++) + { + for (i=1; i<=n[0]; i++) + { + x[ms_I(i,j,k,n)] = (x0[ms_I(i,j,k,n)] + a*( + x[ms_I(i-1,j,k,n)]+x[ms_I(i+1,j,k,n)]+ + x[ms_I(i,j-1,k,n)]+x[ms_I(i,j+1,k,n)]+ + x[ms_I(i,j,k-1,n)]+x[ms_I(i,j,k+1,n)]))/(1+6*a); + } + } + } + } +} + +void multiple_scattering_diffusion(Render *re, VolumePrecache *vp, Material *ma) +{ + const float diff = ma->vol.ms_diff * 0.001f; /* compensate for scaling for a nicer UI range */ + const float simframes = ma->vol.ms_steps; + const int shade_type = ma->vol.shade_type; + float fac = ma->vol.ms_intensity; + + int x, y, z, m; + int *n = vp->res; + const int size = (n[0]+2)*(n[1]+2)*(n[2]+2); + double time, lasttime= PIL_check_seconds_timer(); + float total; + float c=1.0f; + int i; + float origf; /* factor for blending in original light cache */ + float energy_ss, energy_ms; + + float *sr0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); + float *sr=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); + float *sg0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); + float *sg=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); + float *sb0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); + float *sb=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer"); + + total = (float)(n[0]*n[1]*n[2]*simframes); + + energy_ss = total_ss_energy(vp); + + /* Scattering as diffusion pass */ + for (m=0; mdata_r[i] > 0.f) + sr[ms_I(x,y,z,n)] += vp->data_r[i]; + if (vp->data_g[i] > 0.f) + sg[ms_I(x,y,z,n)] += vp->data_g[i]; + if (vp->data_b[i] > 0.f) + sb[ms_I(x,y,z,n)] += vp->data_b[i]; + + /* Displays progress every second */ + if(time-lasttime>1.0f) { + char str[64]; + sprintf(str, "Simulating multiple scattering: %d%%", (int) + (100.0f * (c / total))); + re->i.infostr= str; + re->stats_draw(re->sdh, &re->i); + re->i.infostr= NULL; + lasttime= time; + } + } + } + } + SWAP(float *, sr, sr0); + SWAP(float *, sg, sg0); + SWAP(float *, sb, sb0); + + /* main diffusion simulation */ + ms_diffuse(0, sr0, sr, diff, n); + ms_diffuse(0, sg0, sg, diff, n); + ms_diffuse(0, sb0, sb, diff, n); + + if (re->test_break(re->tbh)) break; + } + + /* normalisation factor to conserve energy */ + energy_ms = total_ms_energy(sr, sg, sb, n); + fac *= (energy_ss / energy_ms); + + /* blend multiple scattering back in the light cache */ + if (shade_type == MA_VOL_SHADE_SINGLEPLUSMULTIPLE) { + /* conserve energy - half single, half multiple */ + origf = 0.5f; + fac *= 0.5f; + } else { + origf = 0.0f; + } + + for (z=1;z<=n[2];z++) + { + for (y=1;y<=n[1];y++) + { + for (x=1;x<=n[0];x++) + { + int index=(x-1)*n[1]*n[2] + (y-1)*n[2] + z-1; + vp->data_r[index] = origf * vp->data_r[index] + fac * sr[ms_I(x,y,z,n)]; + vp->data_g[index] = origf * vp->data_g[index] + fac * sg[ms_I(x,y,z,n)]; + vp->data_b[index] = origf * vp->data_b[index] + fac * sb[ms_I(x,y,z,n)]; + } + } + } + + MEM_freeN(sr0); + MEM_freeN(sr); + MEM_freeN(sg0); + MEM_freeN(sg); + MEM_freeN(sb0); + MEM_freeN(sb); +} + + + +#if 0 // debug stuff +static void *vol_precache_part_test(void *data) +{ + VolPrecachePart *pa = data; + + printf("part number: %d \n", pa->num); + printf("done: %d \n", pa->done); + printf("x min: %d x max: %d \n", pa->minx, pa->maxx); + printf("y min: %d y max: %d \n", pa->miny, pa->maxy); + printf("z min: %d z max: %d \n", pa->minz, pa->maxz); + + return NULL; +} +#endif + +/* Iterate over the 3d voxel grid, and fill the voxels with scattering information + * + * It's stored in memory as 3 big float grids next to each other, one for each RGB channel. + * I'm guessing the memory alignment may work out better this way for the purposes + * of doing linear interpolation, but I haven't actually tested this theory! :) + */ +static void *vol_precache_part(void *data) +{ + VolPrecachePart *pa = (VolPrecachePart *)data; + ObjectInstanceRen *obi = pa->obi; + RayTree *tree = pa->tree; + ShadeInput *shi = pa->shi; + float density, scatter_col[3] = {0.f, 0.f, 0.f}; + float co[3]; + int x, y, z; + const int res[3]= {pa->res[0], pa->res[1], pa->res[2]}; + const float stepsize = vol_get_stepsize(shi, STEPSIZE_VIEW); + + for (z= pa->minz; z < pa->maxz; z++) { + co[2] = pa->bbmin[2] + (pa->voxel[2] * z); + + for (y= pa->miny; y < pa->maxy; y++) { + co[1] = pa->bbmin[1] + (pa->voxel[1] * y); + + for (x=pa->minx; x < pa->maxx; x++) { + co[0] = pa->bbmin[0] + (pa->voxel[0] * x); + + // don't bother if the point is not inside the volume mesh + if (!point_inside_obi(tree, obi, co)) { + obi->volume_precache->data_r[ V_I(x, y, z, res) ] = -1.0f; + obi->volume_precache->data_g[ V_I(x, y, z, res) ] = -1.0f; + obi->volume_precache->data_b[ V_I(x, y, z, res) ] = -1.0f; + continue; + } + + VecCopyf(shi->view, co); + Normalize(shi->view); + density = vol_get_density(shi, co); + vol_get_scattering(shi, scatter_col, co, stepsize, density); + + obi->volume_precache->data_r[ V_I(x, y, z, res) ] = scatter_col[0]; + obi->volume_precache->data_g[ V_I(x, y, z, res) ] = scatter_col[1]; + obi->volume_precache->data_b[ V_I(x, y, z, res) ] = scatter_col[2]; + } + } + } + + pa->done = 1; + + return 0; +} + + +static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Material *ma, ShadeInput *shi) +{ + memset(shi, 0, sizeof(ShadeInput)); + shi->depth= 1; + shi->mask= 1; + shi->mat = ma; + shi->vlr = NULL; + memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h + shi->har= shi->mat->har; + shi->obi= obi; + shi->obr= obi->obr; + shi->lay = re->scene->lay; +} + +static void precache_init_parts(Render *re, RayTree *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts) +{ + VolumePrecache *vp = obi->volume_precache; + int i=0, x, y, z; + float voxel[3]; + int sizex, sizey, sizez; + float *bbmin=obi->obr->boundbox[0], *bbmax=obi->obr->boundbox[1]; + int *res; + int minx, maxx; + int miny, maxy; + int minz, maxz; + + if (!vp) return; + + BLI_freelistN(&re->volume_precache_parts); + + /* currently we just subdivide the box, number of threads per side */ + parts[0] = parts[1] = parts[2] = totthread; + res = vp->res; + + VecSubf(voxel, bbmax, bbmin); + if ((voxel[0] < FLT_EPSILON) || (voxel[1] < FLT_EPSILON) || (voxel[2] < FLT_EPSILON)) + return; + voxel[0] /= res[0]; + voxel[1] /= res[1]; + voxel[2] /= res[2]; + + for (x=0; x < parts[0]; x++) { + sizex = ceil(res[0] / (float)parts[0]); + minx = x * sizex; + maxx = minx + sizex; + maxx = (maxx>res[0])?res[0]:maxx; + + for (y=0; y < parts[1]; y++) { + sizey = ceil(res[1] / (float)parts[1]); + miny = y * sizey; + maxy = miny + sizey; + maxy = (maxy>res[1])?res[1]:maxy; + + for (z=0; z < parts[2]; z++) { + VolPrecachePart *pa= MEM_callocN(sizeof(VolPrecachePart), "new precache part"); + + sizez = ceil(res[2] / (float)parts[2]); + minz = z * sizez; + maxz = minz + sizez; + maxz = (maxz>res[2])?res[2]:maxz; + + pa->done = 0; + pa->working = 0; + + pa->num = i; + pa->tree = tree; + pa->shi = shi; + pa->obi = obi; + VECCOPY(pa->bbmin, bbmin); + VECCOPY(pa->voxel, voxel); + VECCOPY(pa->res, res); + + pa->minx = minx; pa->maxx = maxx; + pa->miny = miny; pa->maxy = maxy; + pa->minz = minz; pa->maxz = maxz; + + + BLI_addtail(&re->volume_precache_parts, pa); + + i++; + } + } + } +} + +static VolPrecachePart *precache_get_new_part(Render *re) +{ + VolPrecachePart *pa, *nextpa=NULL; + + for (pa = re->volume_precache_parts.first; pa; pa=pa->next) + { + if (pa->done==0 && pa->working==0) { + nextpa = pa; + break; + } + } + + return nextpa; +} + +static void precache_resolution(VolumePrecache *vp, float *bbmin, float *bbmax, int res) +{ + float dim[3], div; + + VecSubf(dim, bbmax, bbmin); + + div = MAX3(dim[0], dim[1], dim[2]); + dim[0] /= div; + dim[1] /= div; + dim[2] /= div; + + vp->res[0] = dim[0] * (float)res; + vp->res[1] = dim[1] * (float)res; + vp->res[2] = dim[2] * (float)res; +} + +/* Precache a volume into a 3D voxel grid. + * The voxel grid is stored in the ObjectInstanceRen, + * in camera space, aligned with the ObjectRen's bounding box. + * Resolution is defined by the user. + */ +void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma) +{ + VolumePrecache *vp; + VolPrecachePart *nextpa, *pa; + RayTree *tree; + ShadeInput shi; + ListBase threads; + float *bbmin=obi->obr->boundbox[0], *bbmax=obi->obr->boundbox[1]; + int parts[3], totparts; + + int caching=1, counter=0; + int totthread = re->r.threads; + + double time, lasttime= PIL_check_seconds_timer(); + + R = *re; + + /* create a raytree with just the faces of the instanced ObjectRen, + * used for checking if the cached point is inside or outside. */ + tree = create_raytree_obi(obi, bbmin, bbmax); + if (!tree) return; + + vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache"); + precache_resolution(vp, bbmin, bbmax, ma->vol.precache_resolution); + + vp->data_r = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data red channel"); + vp->data_g = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data green channel"); + vp->data_b = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data blue channel"); + obi->volume_precache = vp; + + /* Need a shadeinput to calculate scattering */ + precache_setup_shadeinput(re, obi, ma, &shi); + + precache_init_parts(re, tree, &shi, obi, totthread, parts); + totparts = parts[0] * parts[1] * parts[2]; + + BLI_init_threads(&threads, vol_precache_part, totthread); + + while(caching) { + + if(BLI_available_threads(&threads) && !(re->test_break(re->tbh))) { + nextpa = precache_get_new_part(re); + if (nextpa) { + nextpa->working = 1; + BLI_insert_thread(&threads, nextpa); + } + } + else PIL_sleep_ms(50); + + caching=0; + counter=0; + for(pa= re->volume_precache_parts.first; pa; pa= pa->next) { + + if(pa->done) { + counter++; + BLI_remove_thread(&threads, pa); + } else + caching = 1; + } + + if (re->test_break(re->tbh) && BLI_available_threads(&threads)==totthread) + caching=0; + + time= PIL_check_seconds_timer(); + if(time-lasttime>1.0f) { + char str[64]; + sprintf(str, "Precaching volume: %d%%", (int)(100.0f * ((float)counter / (float)totparts))); + re->i.infostr= str; + re->stats_draw(re->sdh, &re->i); + re->i.infostr= NULL; + lasttime= time; + } + } + + BLI_end_threads(&threads); + BLI_freelistN(&re->volume_precache_parts); + + if(tree) { + RE_ray_tree_free(tree); + tree= NULL; + } + + lightcache_filter(obi->volume_precache); + + if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SINGLEPLUSMULTIPLE)) + { + multiple_scattering_diffusion(re, vp, ma); + } +} + +int using_lightcache(Material *ma) +{ + return (((ma->vol.shadeflag & MA_VOL_PRECACHESHADING) && (ma->vol.shade_type == MA_VOL_SHADE_SINGLE)) + || (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SINGLEPLUSMULTIPLE))); +} + +/* loop through all objects (and their associated materials) + * marked for pre-caching in convertblender.c, and pre-cache them */ +void volume_precache(Render *re) +{ + ObjectInstanceRen *obi; + VolumeOb *vo; + + for(vo= re->volumes.first; vo; vo= vo->next) { + if (using_lightcache(vo->ma)) { + for(obi= re->instancetable.first; obi; obi= obi->next) { + if (obi->obr == vo->obr) { + vol_precache_objectinstance_threads(re, obi, vo->ma); + } + } + } + } + + re->i.infostr= NULL; + re->stats_draw(re->sdh, &re->i); +} + +void free_volume_precache(Render *re) +{ + ObjectInstanceRen *obi; + + for(obi= re->instancetable.first; obi; obi= obi->next) { + if (obi->volume_precache != NULL) { + MEM_freeN(obi->volume_precache); + MEM_freeN(obi->volume_precache->data_r); + MEM_freeN(obi->volume_precache->data_g); + MEM_freeN(obi->volume_precache->data_b); + } + } + + BLI_freelistN(&re->volumes); +} + +int point_inside_volume_objectinstance(ObjectInstanceRen *obi, float *co) +{ + RayTree *tree; + int inside=0; + + tree = create_raytree_obi(obi, obi->obr->boundbox[0], obi->obr->boundbox[1]); + if (!tree) return 0; + + inside = point_inside_obi(tree, obi, co); + + RE_ray_tree_free(tree); + tree= NULL; + + return inside; +} + diff --git a/source/blender/render/intern/source/volumetric.c b/source/blender/render/intern/source/volumetric.c new file mode 100644 index 00000000000..0e2e3913819 --- /dev/null +++ b/source/blender/render/intern/source/volumetric.c @@ -0,0 +1,688 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Matt Ebb, Raul Fernandez Hernandez (Farsthary) + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" +#include "BLI_voxel.h" + +#include "RE_shader_ext.h" +#include "RE_raytrace.h" + +#include "DNA_material_types.h" +#include "DNA_group_types.h" +#include "DNA_lamp_types.h" + +#include "BKE_global.h" + +#include "render_types.h" +#include "pixelshading.h" +#include "shading.h" +#include "texture.h" +#include "volumetric.h" +#include "volume_precache.h" + +#if defined( _MSC_VER ) && !defined( __cplusplus ) +# define inline __inline +#endif // defined( _MSC_VER ) && !defined( __cplusplus ) + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ +/* only to be used here in this file, it's for speed */ +extern struct Render R; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + +/* tracing */ + +static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, Isect *isect, int intersect_type) +{ + float maxsize = RE_ray_tree_max_size(R.raytree); + + /* XXX TODO - get raytrace max distance from object instance's bounding box */ + /* need to account for scaling only, but keep coords in camera space... + * below code is WIP and doesn't work! + VecSubf(bb_dim, shi->obi->obr->boundbox[1], shi->obi->obr->boundbox[2]); + Mat3MulVecfl(shi->obi->nmat, bb_dim); + maxsize = VecLength(bb_dim); + */ + + VECCOPY(isect->start, co); + isect->end[0] = co[0] + vec[0] * maxsize; + isect->end[1] = co[1] + vec[1] * maxsize; + isect->end[2] = co[2] + vec[2] * maxsize; + + isect->mode= RE_RAY_MIRROR; + isect->oborig= RAY_OBJECT_SET(&R, shi->obi); + isect->face_last= NULL; + isect->ob_last= 0; + isect->lay= -1; + + if (intersect_type == VOL_BOUNDS_DEPTH) isect->faceorig= (RayFace*)shi->vlr; + else if (intersect_type == VOL_BOUNDS_SS) isect->faceorig= NULL; + + if(RE_ray_tree_intersect(R.raytree, isect)) + { + hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; + hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; + hitco[2] = isect->start[2] + isect->labda*isect->vec[2]; + return 1; + } else { + return 0; + } +} + +static void shade_intersection(ShadeInput *shi, float *col, Isect *is) +{ + ShadeInput shi_new; + ShadeResult shr_new; + + memset(&shi_new, 0, sizeof(ShadeInput)); + + shi_new.mask= shi->mask; + shi_new.osatex= shi->osatex; + shi_new.thread= shi->thread; + shi_new.depth = shi->depth + 1; + shi_new.volume_depth= shi->volume_depth + 1; + shi_new.xs= shi->xs; + shi_new.ys= shi->ys; + shi_new.lay= shi->lay; + shi_new.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */ + shi_new.combinedflag= 0xFFFFFF; /* ray trace does all options */ + shi_new.light_override= shi->light_override; + shi_new.mat_override= shi->mat_override; + + VECCOPY(shi_new.camera_co, is->start); + + memset(&shr_new, 0, sizeof(ShadeResult)); + + /* hardcoded limit of 100 for now - prevents problems in weird geometry */ + if (shi->volume_depth < 100) { + shade_ray(is, &shi_new, &shr_new); + } + + VecCopyf(col, shr_new.combined); + col[3] = shr_new.alpha; +} + +static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col) +{ + Isect isect; + float maxsize = RE_ray_tree_max_size(R.raytree); + + VECCOPY(isect.start, co); + isect.end[0] = isect.start[0] + shi->view[0] * maxsize; + isect.end[1] = isect.start[1] + shi->view[1] * maxsize; + isect.end[2] = isect.start[2] + shi->view[2] * maxsize; + + isect.faceorig= (RayFace *)vlr; + + isect.mode= RE_RAY_MIRROR; + isect.oborig= RAY_OBJECT_SET(&R, shi->obi); + isect.face_last= NULL; + isect.ob_last= 0; + isect.lay= -1; + + /* check to see if there's anything behind the volume, otherwise shade the sky */ + if(RE_ray_tree_intersect(R.raytree, &isect)) { + shade_intersection(shi, col, &isect); + } else { + shadeSkyView(col, co, shi->view, NULL, shi->thread); + shadeSunView(col, shi->view); + } +} + +/* input shader data */ + +float vol_get_stepsize(struct ShadeInput *shi, int context) +{ + if (shi->mat->vol.stepsize_type == MA_VOL_STEP_RANDOMIZED) { + /* range between 0.75 and 1.25 */ + const float rnd = 0.5f * BLI_thread_frand(shi->thread) + 0.75f; + + if (context == STEPSIZE_VIEW) + return shi->mat->vol.stepsize * rnd; + else if (context == STEPSIZE_SHADE) + return shi->mat->vol.shade_stepsize * rnd; + } + else { // MA_VOL_STEP_CONSTANT + + if (context == STEPSIZE_VIEW) + return shi->mat->vol.stepsize; + else if (context == STEPSIZE_SHADE) + return shi->mat->vol.shade_stepsize; + } + + return shi->mat->vol.stepsize; +} + +/* trilinear interpolation */ +static void vol_get_precached_scattering(ShadeInput *shi, float *scatter_col, float *co) +{ + VolumePrecache *vp = shi->obi->volume_precache; + float bbmin[3], bbmax[3], dim[3]; + float sample_co[3]; + + if (!vp) return; + + /* convert input coords to 0.0, 1.0 */ + VECCOPY(bbmin, shi->obi->obr->boundbox[0]); + VECCOPY(bbmax, shi->obi->obr->boundbox[1]); + VecSubf(dim, bbmax, bbmin); + + sample_co[0] = ((co[0] - bbmin[0]) / dim[0]); + sample_co[1] = ((co[1] - bbmin[1]) / dim[1]); + sample_co[2] = ((co[2] - bbmin[2]) / dim[2]); + + scatter_col[0] = voxel_sample_trilinear(vp->data_r, vp->res, sample_co); + scatter_col[1] = voxel_sample_trilinear(vp->data_g, vp->res, sample_co); + scatter_col[2] = voxel_sample_trilinear(vp->data_b, vp->res, sample_co); +} + +float vol_get_density(struct ShadeInput *shi, float *co) +{ + float density = shi->mat->vol.density; + float density_scale = shi->mat->vol.density_scale; + float col[3] = {0.0, 0.0, 0.0}; + + do_volume_tex(shi, co, MAP_DENSITY, col, &density); + + return density * density_scale; +} + +/* scattering multiplier, values above 1.0 are non-physical, + * but can be useful to tweak lighting */ +float vol_get_scattering_fac(ShadeInput *shi, float *co) +{ + float scatter = shi->mat->vol.scattering; + float col[3] = {0.0, 0.0, 0.0}; + + do_volume_tex(shi, co, MAP_SCATTERING, col, &scatter); + + return scatter; +} + +/* compute emission component, amount of radiance to add per segment + * can be textured with 'emit' */ +void vol_get_emission(ShadeInput *shi, float *emission_col, float *co, float density) +{ + float emission = shi->mat->vol.emission; + VECCOPY(emission_col, shi->mat->vol.emission_col); + + do_volume_tex(shi, co, MAP_EMISSION+MAP_EMISSION_COL, emission_col, &emission); + + emission_col[0] = emission_col[0] * emission * density; + emission_col[1] = emission_col[1] * emission * density; + emission_col[2] = emission_col[2] * emission * density; +} + +void vol_get_absorption(ShadeInput *shi, float *absorb_col, float *co) +{ + float absorption = shi->mat->vol.absorption; + VECCOPY(absorb_col, shi->mat->vol.absorption_col); + + do_volume_tex(shi, co, MAP_ABSORPTION+MAP_ABSORPTION_COL, absorb_col, &absorption); + + absorb_col[0] = (1.0f - absorb_col[0]) * absorption; + absorb_col[1] = (1.0f - absorb_col[1]) * absorption; + absorb_col[2] = (1.0f - absorb_col[2]) * absorption; +} + + +/* phase function - determines in which directions the light + * is scattered in the volume relative to incoming direction + * and view direction */ +float vol_get_phasefunc(ShadeInput *shi, short phasefunc_type, float g, float *w, float *wp) +{ + const float costheta = Inpf(w, wp); + const float scale = M_PI; + + /* + * Scale constant is required, since Blender's shading system doesn't normalise for + * energy conservation - eg. scaling by 1/pi for a lambert shader. + * This makes volumes darker than other solid objects, for the same lighting intensity. + * To correct this, scale up the phase function values + * until Blender's shading system supports this better. --matt + */ + + switch (phasefunc_type) { + case MA_VOL_PH_MIEHAZY: + return scale * (0.5f + 4.5f * powf(0.5 * (1.f + costheta), 8.f)) / (4.f*M_PI); + case MA_VOL_PH_MIEMURKY: + return scale * (0.5f + 16.5f * powf(0.5 * (1.f + costheta), 32.f)) / (4.f*M_PI); + case MA_VOL_PH_RAYLEIGH: + return scale * 3.f/(16.f*M_PI) * (1 + costheta * costheta); + case MA_VOL_PH_HG: + return scale * (1.f / (4.f * M_PI) * (1.f - g*g) / powf(1.f + g*g - 2.f * g * costheta, 1.5f)); + case MA_VOL_PH_SCHLICK: + { + const float k = 1.55f * g - .55f * g * g * g; + const float kcostheta = k * costheta; + return scale * (1.f / (4.f * M_PI) * (1.f - k*k) / ((1.f - kcostheta) * (1.f - kcostheta))); + } + case MA_VOL_PH_ISOTROPIC: + default: + return scale * (1.f / (4.f * M_PI)); + } +} + +/* Compute attenuation, otherwise known as 'optical thickness', extinction, or tau. + * Used in the relationship Transmittance = e^(-attenuation) + */ +void vol_get_attenuation_seg(ShadeInput *shi, float *transmission, float stepsize, float *co, float density) +{ + /* input density = density at co */ + float tau[3] = {0.f, 0.f, 0.f}; + float absorb_col[3]; + + vol_get_absorption(shi, absorb_col, co); + + /* homogenous volume within the sampled distance */ + tau[0] = stepsize * density * absorb_col[0]; + tau[1] = stepsize * density * absorb_col[1]; + tau[2] = stepsize * density * absorb_col[2]; + + transmission[0] *= exp(-tau[0]); + transmission[1] *= exp(-tau[1]); + transmission[2] *= exp(-tau[2]); +} + +/* Compute attenuation, otherwise known as 'optical thickness', extinction, or tau. + * Used in the relationship Transmittance = e^(-attenuation) + */ +void vol_get_attenuation(ShadeInput *shi, float *transmission, float *co, float *endco, float density, float stepsize) +{ + /* input density = density at co */ + float tau[3] = {0.f, 0.f, 0.f}; + float absorb_col[3]; + int s, nsteps; + float step_vec[3], step_sta[3], step_end[3]; + const float dist = VecLenf(co, endco); + + vol_get_absorption(shi, absorb_col, co); + + nsteps = (int)((dist / stepsize) + 0.5); + + VecSubf(step_vec, endco, co); + VecMulf(step_vec, 1.0f / nsteps); + + VecCopyf(step_sta, co); + VecAddf(step_end, step_sta, step_vec); + + for (s = 0; s < nsteps; s++) { + if (s > 0) + density = vol_get_density(shi, step_sta); + + tau[0] += stepsize * density; + tau[1] += stepsize * density; + tau[2] += stepsize * density; + + if (s < nsteps-1) { + VecCopyf(step_sta, step_end); + VecAddf(step_end, step_end, step_vec); + } + } + VecMulVecf(tau, tau, absorb_col); + + transmission[0] *= exp(-tau[0]); + transmission[1] *= exp(-tau[1]); + transmission[2] *= exp(-tau[2]); +} + +void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float *lacol, float stepsize, float density) +{ + float visifac, lv[3], lampdist; + float tr[3]={1.0,1.0,1.0}; + float hitco[3], *atten_co; + float p; + float scatter_fac; + float shade_stepsize = vol_get_stepsize(shi, STEPSIZE_SHADE); + + if (lar->mode & LA_LAYER) if((lar->lay & shi->obi->lay)==0) return; + if ((lar->lay & shi->lay)==0) return; + if (lar->energy == 0.0) return; + + if ((visifac= lamp_get_visibility(lar, co, lv, &lampdist)) == 0.f) return; + + VecCopyf(lacol, &lar->r); + + if(lar->mode & LA_TEXTURE) { + shi->osatex= 0; + do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE); + } + + VecMulf(lacol, visifac*lar->energy); + + if (ELEM(lar->type, LA_SUN, LA_HEMI)) + VECCOPY(lv, lar->vec); + VecMulf(lv, -1.0f); + + if (shi->mat->vol.shade_type != MA_VOL_SHADE_NONE) { + Isect is; + + /* find minimum of volume bounds, or lamp coord */ + if (vol_get_bounds(shi, co, lv, hitco, &is, VOL_BOUNDS_SS)) { + float dist = VecLenf(co, hitco); + VlakRen *vlr = (VlakRen *)is.face; + + /* simple internal shadowing */ + if (vlr->mat->material_type == MA_TYPE_SURFACE) { + lacol[0] = lacol[1] = lacol[2] = 0.0f; + return; + } + + if (ELEM(lar->type, LA_SUN, LA_HEMI)) + /* infinite lights, can never be inside volume */ + atten_co = hitco; + else if ( lampdist < dist ) { + atten_co = lar->co; + } else + atten_co = hitco; + + vol_get_attenuation(shi, tr, co, atten_co, density, shade_stepsize); + + VecMulVecf(lacol, lacol, tr); + } + else { + /* Point is on the outside edge of the volume, + * therefore no attenuation, full transmission. + * Radiance from lamp remains unchanged */ + } + } + + p = vol_get_phasefunc(shi, shi->mat->vol.phasefunc_type, shi->mat->vol.phasefunc_g, shi->view, lv); + VecMulf(lacol, p); + + scatter_fac = vol_get_scattering_fac(shi, co); + VecMulf(lacol, scatter_fac); +} + +/* single scattering only for now */ +void vol_get_scattering(ShadeInput *shi, float *scatter_col, float *co, float stepsize, float density) +{ + ListBase *lights; + GroupObject *go; + LampRen *lar; + + scatter_col[0] = scatter_col[1] = scatter_col[2] = 0.f; + + lights= get_lights(shi); + for(go=lights->first; go; go= go->next) + { + float lacol[3] = {0.f, 0.f, 0.f}; + lar= go->lampren; + + if (lar) { + vol_shade_one_lamp(shi, co, lar, lacol, stepsize, density); + VecAddf(scatter_col, scatter_col, lacol); + } + } +} + + +/* +The main volumetric integrator, using an emission/absorption/scattering model. + +Incoming radiance = + +outgoing radiance from behind surface * beam transmittance/attenuation ++ added radiance from all points along the ray due to participating media + --> radiance for each segment = + (radiance added by scattering + radiance added by emission) * beam transmittance/attenuation +*/ +static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float *endco) +{ + float tr[3] = {1.0f, 1.0f, 1.0f}; + float radiance[3] = {0.f, 0.f, 0.f}, d_radiance[3] = {0.f, 0.f, 0.f}; + float stepsize = vol_get_stepsize(shi, STEPSIZE_VIEW); + int nsteps, s; + float emit_col[3], scatter_col[3] = {0.0, 0.0, 0.0}; + float stepvec[3], step_sta[3], step_end[3], step_mid[3]; + float density; + const float depth_cutoff = shi->mat->vol.depth_cutoff; + + /* ray marching */ + nsteps = (int)((VecLenf(co, endco) / stepsize) + 0.5); + + VecSubf(stepvec, endco, co); + VecMulf(stepvec, 1.0f / nsteps); + VecCopyf(step_sta, co); + VecAddf(step_end, step_sta, stepvec); + + /* get radiance from all points along the ray due to participating media */ + for (s = 0; s < nsteps; s++) { + + density = vol_get_density(shi, step_sta); + + /* there's only any use in shading here if there's actually some density to shade! */ + if (density > 0.01f) { + + /* transmittance component (alpha) */ + vol_get_attenuation_seg(shi, tr, stepsize, co, density); + + step_mid[0] = step_sta[0] + (stepvec[0] * 0.5); + step_mid[1] = step_sta[1] + (stepvec[1] * 0.5); + step_mid[2] = step_sta[2] + (stepvec[2] * 0.5); + + /* incoming light via emission or scattering (additive) */ + vol_get_emission(shi, emit_col, step_mid, density); + + if (using_lightcache(shi->mat)) { + vol_get_precached_scattering(shi, scatter_col, step_mid); + } else + vol_get_scattering(shi, scatter_col, step_mid, stepsize, density); + + VecMulf(scatter_col, density); + VecAddf(d_radiance, emit_col, scatter_col); + + /* Lv += Tr * (Lve() + Ld) */ + VecMulVecf(d_radiance, tr, d_radiance); + VecMulf(d_radiance, stepsize); + + VecAddf(radiance, radiance, d_radiance); + } + + VecCopyf(step_sta, step_end); + VecAddf(step_end, step_end, stepvec); + + /* luminance rec. 709 */ + if ((0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]) < depth_cutoff) break; + } + + /* multiply original color (behind volume) with beam transmittance over entire distance */ + VecMulVecf(col, tr, col); + VecAddf(col, col, radiance); + + /* alpha <-- transmission luminance */ + col[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]); +} + +/* the main entry point for volume shading */ +static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int inside_volume) +{ + float hitco[3], col[4] = {0.f,0.f,0.f,0.f}; + float *startco, *endco; + int trace_behind = 1; + const int ztransp= ((shi->depth==0) && (shi->mat->mode & MA_TRANSP) && (shi->mat->mode & MA_ZTRANSP)); + Isect is; + + /* check for shading an internal face a volume object directly */ + if (inside_volume == VOL_SHADE_INSIDE) + trace_behind = 0; + else if (inside_volume == VOL_SHADE_OUTSIDE) { + if (shi->flippednor) + inside_volume = VOL_SHADE_INSIDE; + } + + if (ztransp && inside_volume == VOL_SHADE_INSIDE) { + MatInside *mi; + int render_this=0; + + /* don't render the backfaces of ztransp volume materials. + + * volume shading renders the internal volume from between the + * near view intersection of the solid volume to the + * intersection on the other side, as part of the shading of + * the front face. + + * Because ztransp renders both front and back faces independently + * this will double up, so here we prevent rendering the backface as well, + * which would otherwise render the volume in between the camera and the backface + * --matt */ + + for (mi=R.render_volumes_inside.first; mi; mi=mi->next) { + /* weak... */ + if (mi->ma == shi->mat) render_this=1; + } + if (!render_this) return; + } + + + if (inside_volume == VOL_SHADE_INSIDE) + { + startco = shi->camera_co; + endco = shi->co; + + if (trace_behind) { + if (!ztransp) + /* trace behind the volume object */ + vol_trace_behind(shi, shi->vlr, endco, col); + } else { + /* we're tracing through the volume between the camera + * and a solid surface, so use that pre-shaded radiance */ + QUATCOPY(col, shr->combined); + } + + /* shade volume from 'camera' to 1st hit point */ + volumeintegrate(shi, col, startco, endco); + } + /* trace to find a backface, the other side bounds of the volume */ + /* (ray intersect ignores front faces here) */ + else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) + { + VlakRen *vlr = (VlakRen *)is.face; + + startco = shi->co; + endco = hitco; + + if (!ztransp) { + /* if it's another face in the same material */ + if (vlr->mat == shi->mat) { + /* trace behind the 2nd (raytrace) hit point */ + vol_trace_behind(shi, (VlakRen *)is.face, endco, col); + } else { + shade_intersection(shi, col, &is); + } + } + + /* shade volume from 1st hit point to 2nd hit point */ + volumeintegrate(shi, col, startco, endco); + } + + if (ztransp) + col[3] = col[3]>1.f?1.f:col[3]; + else + col[3] = 1.f; + + VecCopyf(shr->combined, col); + shr->alpha = col[3]; + + VECCOPY(shr->diff, shr->combined); +} + +/* Traces a shadow through the object, + * pretty much gets the transmission over a ray path */ +void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is) +{ + float hitco[3]; + float tr[3] = {1.0,1.0,1.0}; + Isect is; + float shade_stepsize = vol_get_stepsize(shi, STEPSIZE_SHADE); + float *startco, *endco; + float density=0.f; + + memset(shr, 0, sizeof(ShadeResult)); + + /* if 1st hit normal is facing away from the camera, + * then we're inside the volume already. */ + if (shi->flippednor) { + startco = last_is->start; + endco = shi->co; + } + /* trace to find a backface, the other side bounds of the volume */ + /* (ray intersect ignores front faces here) */ + else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) { + startco = shi->co; + endco = hitco; + } + else { + shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f; + shr->alpha = shr->combined[3] = 1.f; + return; + } + + density = vol_get_density(shi, startco); + vol_get_attenuation(shi, tr, startco, endco, density, shade_stepsize); + + VecCopyf(shr->combined, tr); + shr->combined[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]); + shr->alpha = shr->combined[3]; +} + + +/* delivers a fully filled in ShadeResult, for all passes */ +void shade_volume_outside(ShadeInput *shi, ShadeResult *shr) +{ + memset(shr, 0, sizeof(ShadeResult)); + volume_trace(shi, shr, VOL_SHADE_OUTSIDE); +} + + +void shade_volume_inside(ShadeInput *shi, ShadeResult *shr) +{ + MatInside *m; + Material *mat_backup; + + //if (BLI_countlist(&R.render_volumes_inside) == 0) return; + + /* XXX: extend to multiple volumes perhaps later */ + mat_backup = shi->mat; + m = R.render_volumes_inside.first; + shi->mat = m->ma; + + volume_trace(shi, shr, VOL_SHADE_INSIDE); + + shi->mat = mat_backup; +} \ No newline at end of file diff --git a/source/blender/render/intern/source/voxeldata.c b/source/blender/render/intern/source/voxeldata.c new file mode 100644 index 00000000000..ff076579788 --- /dev/null +++ b/source/blender/render/intern/source/voxeldata.c @@ -0,0 +1,341 @@ +/** + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Raul Fernandez Hernandez (Farsthary), Matt Ebb. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_voxel.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_main.h" + +#include "smoke_API.h" + +#include "DNA_texture_types.h" +#include "DNA_object_types.h" +#include "DNA_modifier_types.h" +#include "DNA_smoke_types.h" + + +#include "render_types.h" +#include "renderdatabase.h" +#include "texture.h" +#include "voxeldata.h" + +void load_frame_blendervoxel(FILE *fp, float *F, int size, int frame, int offset) +{ + fseek(fp,frame*size*sizeof(float)+offset,0); + fread(F,sizeof(float),size,fp); +} + +void load_frame_raw8(FILE *fp, float *F, int size, int frame) +{ + char *tmp; + int i; + + tmp = (char *)MEM_mallocN(sizeof(char)*size, "temporary voxel file reading storage"); + + fseek(fp,(frame-1)*size*sizeof(char),0); + fread(tmp, sizeof(char), size, fp); + + for (i=0; iima; + ImageUser *iuser = &tex->iuser; + int x=0, y=0, z=0; + float *rf; + + if (!ima || !iuser) return; + + ima->source = IMA_SRC_SEQUENCE; + iuser->framenr = 1 + iuser->offset; + + /* find the first valid ibuf and use it to initialise the resolution of the data set */ + /* need to do this in advance so we know how much memory to allocate */ + ibuf= BKE_image_get_ibuf(ima, iuser); + while (!ibuf && (iuser->framenr < iuser->frames)) { + iuser->framenr++; + ibuf= BKE_image_get_ibuf(ima, iuser); + } + if (!ibuf) return; + if (!ibuf->rect_float) IMB_float_from_rect(ibuf); + + vd->flag |= TEX_VD_STILL; + vd->resol[0] = ibuf->x; + vd->resol[1] = ibuf->y; + vd->resol[2] = iuser->frames; + vd->dataset = MEM_mapallocN(sizeof(float)*(vd->resol[0])*(vd->resol[1])*(vd->resol[2]), "voxel dataset"); + + for (z=0; z < iuser->frames; z++) + { + /* get a new ibuf for each frame */ + if (z > 0) { + iuser->framenr++; + ibuf= BKE_image_get_ibuf(ima, iuser); + if (!ibuf) break; + if (!ibuf->rect_float) IMB_float_from_rect(ibuf); + } + rf = ibuf->rect_float; + + for (y=0; y < ibuf->y; y++) + { + for (x=0; x < ibuf->x; x++) + { + /* currently converted to monchrome */ + vd->dataset[ V_I(x, y, z, vd->resol) ] = (rf[0] + rf[1] + rf[2])*0.333f; + rf +=4; + } + } + + BKE_image_free_anim_ibufs(ima, iuser->framenr); + } +} + +void write_voxeldata_header(struct VoxelDataHeader *h, FILE *fp) +{ + fwrite(h,sizeof(struct VoxelDataHeader),1,fp); +} + +void read_voxeldata_header(FILE *fp, struct VoxelData *vd) +{ + VoxelDataHeader *h=(VoxelDataHeader *)MEM_mallocN(sizeof(VoxelDataHeader), "voxel data header"); + + rewind(fp); + fread(h,sizeof(VoxelDataHeader),1,fp); + + vd->resol[0]=h->resolX; + vd->resol[1]=h->resolY; + vd->resol[2]=h->resolZ; + + MEM_freeN(h); +} + +void init_frame_smoke(Render *re, VoxelData *vd, Tex *tex) +{ + Object *ob; + ModifierData *md; + + vd->dataset = NULL; + if (vd->object == NULL) return; + ob= vd->object; + + /* draw code for smoke */ + if(md = (ModifierData *)modifiers_findByType(ob, eModifierType_Smoke)) + { + SmokeModifierData *smd = (SmokeModifierData *)md; + + if(smd->domain && smd->domain->fluid) { + //int big = (smd->domain->flags & MOD_SMOKE_HIGHRES); + int big=0; + + if (big) { + //smoke_turbulence_get_res(smd->domain->wt, vd->resol); + //vd->dataset = smoke_turbulence_get_density(smd->domain->wt); + } else { + VECCOPY(vd->resol, smd->domain->res); + vd->dataset = smoke_get_density(smd->domain->fluid); + } + } + } +} + +void cache_voxeldata(struct Render *re,Tex *tex) +{ + VoxelData *vd = tex->vd; + FILE *fp; + int size; + int curframe; + + if (!vd) return; + + /* image sequence gets special treatment */ + if (vd->file_format == TEX_VD_IMAGE_SEQUENCE) { + load_frame_image_sequence(re, vd, tex); + return; + } else if (vd->file_format == TEX_VD_SMOKE) { + init_frame_smoke(re, vd, tex); + return; + } + + if (!BLI_exists(vd->source_path)) return; + fp = fopen(vd->source_path,"rb"); + if (!fp) return; + + if (vd->file_format == TEX_VD_BLENDERVOXEL) + read_voxeldata_header(fp, vd); + + size = (vd->resol[0])*(vd->resol[1])*(vd->resol[2]); + vd->dataset = MEM_mapallocN(sizeof(float)*size, "voxel dataset"); + + if (vd->flag & TEX_VD_STILL) curframe = vd->still_frame; + else curframe = re->r.cfra; + + switch(vd->file_format) { + case TEX_VD_BLENDERVOXEL: + load_frame_blendervoxel(fp, vd->dataset, size, curframe-1, sizeof(VoxelDataHeader)); + break; + case TEX_VD_RAW_8BIT: + load_frame_raw8(fp, vd->dataset, size, curframe); + break; + } + + fclose(fp); +} + +void make_voxeldata(struct Render *re) +{ + Tex *tex; + + if(re->scene->r.scemode & R_PREVIEWBUTS) + return; + + re->i.infostr= "Loading voxel datasets"; + re->stats_draw(re->sdh, &re->i); + + /* XXX: should be doing only textures used in this render */ + for (tex= G.main->tex.first; tex; tex= tex->id.next) { + if(tex->id.us && tex->type==TEX_VOXELDATA) { + cache_voxeldata(re, tex); + } + } + + re->i.infostr= NULL; + re->stats_draw(re->sdh, &re->i); + +} + +static void free_voxeldata_one(Render *re, Tex *tex) +{ + VoxelData *vd = tex->vd; + + if (vd->dataset) { + MEM_freeN(vd->dataset); + vd->dataset = NULL; + } +} + + +void free_voxeldata(Render *re) +{ + Tex *tex; + + if(re->scene->r.scemode & R_PREVIEWBUTS) + return; + + for (tex= G.main->tex.first; tex; tex= tex->id.next) { + if(tex->id.us && tex->type==TEX_VOXELDATA) { + free_voxeldata_one(re, tex); + } + } +} + +int voxeldatatex(struct Tex *tex, float *texvec, struct TexResult *texres) +{ + int retval = TEX_INT; + VoxelData *vd = tex->vd; + float co[3], offset[3] = {0.5, 0.5, 0.5}; + + if ((!vd) || (vd->dataset==NULL)) { + texres->tin = 0.0f; + return 0; + } + + /* scale lookup from 0.0-1.0 (original location) to -1.0, 1.0, consistent with image texture tex coords */ + /* in implementation this works backwards, bringing sample locations from -1.0, 1.0 + * to the range 0.0, 1.0, before looking up in the voxel structure. */ + VecCopyf(co, texvec); + VecMulf(co, 0.5f); + VecAddf(co, co, offset); + + /* co is now in the range 0.0, 1.0 */ + switch (tex->extend) { + case TEX_CLIP: + { + 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)) { + texres->tin = 0.f; + return retval; + } + break; + } + case TEX_REPEAT: + { + co[0] = co[0] - floor(co[0]); + co[1] = co[1] - floor(co[1]); + co[2] = co[2] - floor(co[2]); + break; + } + case TEX_EXTEND: + { + CLAMP(co[0], 0.f, 1.f); + CLAMP(co[1], 0.f, 1.f); + CLAMP(co[2], 0.f, 1.f); + break; + } + } + + switch (vd->interp_type) { + case TEX_VD_NEARESTNEIGHBOR: + texres->tin = voxel_sample_nearest(vd->dataset, vd->resol, co); + break; + case TEX_VD_LINEAR: + texres->tin = voxel_sample_trilinear(vd->dataset, vd->resol, co); + break; + case TEX_VD_TRICUBIC: + texres->tin = voxel_sample_tricubic(vd->dataset, vd->resol, co); + break; + } + + texres->tin *= vd->int_multiplier; + BRICONT; + + texres->tr = texres->tin; + texres->tg = texres->tin; + texres->tb = texres->tin; + texres->ta = texres->tin; + BRICONTRGB; + + return retval; +} + +