diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 2f933fb5771..9d5221b4e92 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -43,6 +43,7 @@ _modules = ( "properties_particle", "properties_physics_cloth", "properties_physics_common", + "properties_physics_dynamicpaint", "properties_physics_field", "properties_physics_fluid", "properties_physics_smoke", diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 75069993521..4e40db267de 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -229,6 +229,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel): row.prop(md, "mid_level") row.prop(md, "strength") + def DYNAMIC_PAINT(self, layout, ob, md): + layout.label(text="See Dynamic Paint panel.") + def EDGE_SPLIT(self, layout, ob, md): split = layout.split() diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py index f7cf8da1840..0030abff71b 100644 --- a/release/scripts/startup/bl_ui/properties_physics_common.py +++ b/release/scripts/startup/bl_ui/properties_physics_common.py @@ -64,6 +64,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, bpy.types.Panel): if(ob.type == 'MESH'): physics_add(self, col, context.collision, "Collision", 'COLLISION', 'MOD_PHYSICS', False) physics_add(self, col, context.cloth, "Cloth", 'CLOTH', 'MOD_CLOTH', True) + physics_add(self, col, context.dynamic_paint, "Dynamic Paint", 'DYNAMIC_PAINT', 'MOD_FLUIDSIM', True) col = split.column() diff --git a/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py new file mode 100644 index 00000000000..a564704e80f --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py @@ -0,0 +1,264 @@ +# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# +import bpy + + +class PhysicButtonsPanel(): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "physics" + + @classmethod + def poll(cls, context): + ob = context.object + rd = context.scene.render + return (ob and ob.type == 'MESH') and (not rd.use_game_engine) and (context.dynamic_paint) + + +class PHYSICS_PT_dynamic_paint(PhysicButtonsPanel, bpy.types.Panel): + bl_label = "Dynamic Paint" + + def draw(self, context): + layout = self.layout + + md = context.dynamic_paint + ob = context.object + + if md: + layout.prop(md, "dynamicpaint_type", expand=True) + + if md.dynamicpaint_type == 'CANVAS': + canvas = md.canvas_settings + + layout.operator("dpaint.bake", text="Bake Dynamic Paint", icon='MOD_FLUIDSIM') + if len(canvas.ui_info) != 0: + layout.label(text=canvas.ui_info) + + col = layout.column() + col.label(text="Quality:") + col.prop(canvas, "resolution") + col.prop(canvas, "use_anti_aliasing") + + col = layout.column() + col.label(text="Frames:") + split = col.split() + + col = split.column(align=True) + col.prop(canvas, "start_frame", text="Start") + col.prop(canvas, "end_frame", text="End") + + col = split.column() + col.prop(canvas, "substeps") + + + elif md.dynamicpaint_type == 'PAINT': + paint = md.paint_settings + + layout.prop(paint, "do_paint") + + split = layout.split() + + col = split.column() + col.active = paint.do_paint + col.prop(paint, "absolute_alpha") + col.prop(paint, "paint_erase") + col.prop(paint, "paint_wetness", text="Wetness") + + col = split.column() + col.active = paint.do_paint + sub = col.column() + sub.active = (paint.paint_source != "PSYS"); + sub.prop(paint, "use_material") + if paint.use_material and paint.paint_source != "PSYS": + col.prop(paint, "material", text="") + else: + col.prop(paint, "paint_color", text="") + col.prop(paint, "paint_alpha", text="Alpha") + + layout.label() + layout.prop(paint, "do_displace") + + +class PHYSICS_PT_dp_output(PhysicButtonsPanel, bpy.types.Panel): + bl_label = "Dynamic Paint: Output" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + md = context.dynamic_paint + return md and (md.dynamicpaint_type == 'CANVAS') + + def draw(self, context): + layout = self.layout + + canvas = context.dynamic_paint.canvas_settings + + col = layout.column() + col.prop(canvas, "output_paint") + sub = col.column() + sub.active = canvas.output_paint + sub.prop(canvas, "paint_output_path", text="") + sub.prop(canvas, "premultiply", text="Premultiply alpha") + + layout.separator() + + col = layout.column() + col.prop(canvas, "output_wet") + sub = col.column() + sub.active = canvas.output_wet + sub.prop(canvas, "wet_output_path", text="") + + layout.separator() + + col = layout.column() + col.prop(canvas, "output_disp") + sub = col.column() + sub.active = canvas.output_disp + sub.prop(canvas, "displace_output_path", text="") + sub.prop(canvas, "displacement", text="Strength") + + split = sub.split() + sub = split.column() + sub.prop(canvas, "disp_type", text="Type") + sub = split.column() + sub.prop(canvas, "disp_format", text="Format") + + +class PHYSICS_PT_dp_advanced_canvas(PhysicButtonsPanel, bpy.types.Panel): + bl_label = "Dynamic Paint: Advanced" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + md = context.dynamic_paint + return md and (md.dynamicpaint_type == 'CANVAS') + + def draw(self, context): + layout = self.layout + + canvas = context.dynamic_paint.canvas_settings + ob = context.object + + col = layout.column() + split = col.split(percentage=0.7) + split.prop(canvas, "dry_speed", text="Dry Time") + split.prop(canvas, "use_dry_log", text="Slow") + + col = layout.column() + col.prop(canvas, "use_dissolve_paint") + sub = col.column() + sub.active = canvas.use_dissolve_paint + sub.prop(canvas, "dissolve_speed", text="Time") + + col = layout.column() + col.prop(canvas, "use_flatten_disp", text="Flatten Displace") + sub = col.column() + sub.active = canvas.use_flatten_disp + sub.prop(canvas, "flatten_speed", text="Time") + + layout.separator() + + layout.prop_search(canvas, "uv_layer", ob.data, "uv_textures") + +class PHYSICS_PT_dp_effects(PhysicButtonsPanel, bpy.types.Panel): + bl_label = "Dynamic Paint: Effects" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + md = context.dynamic_paint + return md and (md.dynamicpaint_type == 'CANVAS') + + def draw(self, context): + layout = self.layout + + canvas = context.dynamic_paint.canvas_settings + + layout.prop(canvas, "effect_ui", expand=True) + + if canvas.effect_ui == "SPREAD": + layout.prop(canvas, "use_spread") + col = layout.column() + col.active = canvas.use_spread + col.prop(canvas, "spread_speed") + + elif canvas.effect_ui == "DRIP": + layout.prop(canvas, "use_drip") + col = layout.column() + col.active = canvas.use_drip + col.prop(canvas, "drip_speed") + + elif canvas.effect_ui == "SHRINK": + layout.prop(canvas, "use_shrink") + col = layout.column() + col.active = canvas.use_shrink + col.prop(canvas, "shrink_speed") + +class PHYSICS_PT_dp_advanced_paint(PhysicButtonsPanel, bpy.types.Panel): + bl_label = "Dynamic Paint: Advanced" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + md = context.dynamic_paint + return md and (md.dynamicpaint_type == 'PAINT') + + def draw(self, context): + layout = self.layout + + paint = context.dynamic_paint.paint_settings + ob = context.object + + split = layout.split() + col = split.column() + col.prop(paint, "paint_source") + + if paint.paint_source == "PSYS": + col.prop_search(paint, "psys", ob, "particle_systems", text="") + if paint.psys: + col.label(text="Particle effect:") + sub = col.column() + sub.active = not paint.use_part_radius + sub.prop(paint, "solid_radius", text="Solid Radius") + col.prop(paint, "use_part_radius", text="Use Particle's Radius") + col.prop(paint, "smooth_radius", text="Smooth radius") + + elif paint.paint_source == "DISTANCE" or paint.paint_source == "VOLDIST": + col.prop(paint, "paint_distance", text="Paint Distance") + split = layout.row().split() + sub = split.column() + sub.prop(paint, "prox_facealigned", text="Face Aligned") + sub = split.column() + sub.prop(paint, "prox_falloff", text="Falloff") + if paint.prox_falloff == "RAMP": + col = layout.row().column() + col.label(text="Falloff Ramp:") + col.prop(paint, "prox_ramp_alpha", text="Only Use Alpha") + col.template_color_ramp(paint, "paint_ramp", expand=True) + +def register(): + bpy.utils.register_module(__name__) + + +def unregister(): + bpy.utils.register_module(__name__) + +if __name__ == "__main__": + register() diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 29487713ad4..8d373da6897 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -108,6 +108,11 @@ BVHTree* bvhtree_from_mesh_edges(struct BVHTreeFromMesh *data, struct DerivedMes */ void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data); +/* +* Math functions used by callbacks +*/ +float ray_tri_intersection(const BVHTreeRay *ray, const float m_dist, const float *v0, const float *v1, const float *v2); +float nearest_point_in_tri_surface(const float *v0,const float *v1,const float *v2,const float *p, int *v, int *e, float *nearest ); /* * BVHCache diff --git a/source/blender/blenkernel/BKE_dynamicpaint.h b/source/blender/blenkernel/BKE_dynamicpaint.h new file mode 100644 index 00000000000..b0d97e9680b --- /dev/null +++ b/source/blender/blenkernel/BKE_dynamicpaint.h @@ -0,0 +1,88 @@ +/** + * ***** 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. + * + * Contributor(s): Miika Hämäläinen + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BKE_DYNAMIC_PAINT_H_ +#define BKE_DYNAMIC_PAINT_H_ + +typedef struct FaceAdv { + float no[3]; + float no_q[3]; +} FaceAdv; + +typedef struct BB2d { + float min[2], max[2]; +} BB2d; + +typedef struct Vec3f { + float v[3]; +} Vec3f; + + +/* Actual surface point */ +typedef struct PaintSurfacePoint { + /* + * Paint layer data + */ + float color[3]; + float alpha; + float depth; /* displacement */ + + /* + * Effect / moving layer data + * ! Only generated if effects enabled ! + */ + int neighbours[8]; /* Indexes of 8 neighbouring pixels if exist */ + float neighbour_dist[8]; /* Distances to all 8 neighbouring pixels */ + float gravity_dir; /* UV space direction of gravity */ + float gravity_rate; /* Gravity strength. (Depends on surface angle.) */ + + + /* Wet paint is handled at effect layer only + * and mixed to surface when drying */ + float e_color[3]; + float e_alpha; + float wetness; + short state; /* 0 = empty or dry + * 1 = wet paint + * 2 = new paint */ + + + /* + * Pixel / mesh data + */ + int index; /* face index on domain derived mesh */ + int v1, v2, v3; /* vertex indexes */ + + int neighbour_pixel; /* If this pixel isn't uv mapped to any face, + but it's neighbouring pixel is */ + short quad; + struct Vec3f *barycentricWeights; /* b-weights for all pixel samples */ + float realCoord[3]; /* current pixel center world-space coordinates */ + float invNorm[3]; /*current pixel world-space inverted normal. depends on smooth/flat shading */ + +} PaintSurfacePoint; + +typedef struct PaintSurface { + + struct PaintSurfacePoint *point; + int w, h, active_points; + short pixelSamples; +} PaintSurface; + +void dynamicPaint_Modifier_do(struct DynamicPaintModifierData *pmd, struct Scene *scene, struct Object *ob, struct DerivedMesh *dm); + +void dynamicPaint_Modifier_free (struct DynamicPaintModifierData *pmd); +void dynamicPaint_Modifier_createType(struct DynamicPaintModifierData *pmd); +void dynamicPaint_Modifier_copy(struct DynamicPaintModifierData *pmd, struct DynamicPaintModifierData *tsmd); + +#endif /* BKE_DYNAMIC_PAINT_H_ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 2831636361f..c7e4f20d7ad 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -90,6 +90,7 @@ set(SRC intern/deform.c intern/depsgraph.c intern/displist.c + intern/dynamicpaint.c intern/effect.c intern/fcurve.c intern/fluidsim.c diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c index 5520e4d1d41..f49f1aea0b8 100644 --- a/source/blender/blenkernel/intern/bvhutils.c +++ b/source/blender/blenkernel/intern/bvhutils.c @@ -50,7 +50,7 @@ /* Math stuff for ray casting on mesh faces and for nearest surface */ -static float ray_tri_intersection(const BVHTreeRay *ray, const float UNUSED(m_dist), const float *v0, const float *v1, const float *v2) +float ray_tri_intersection(const BVHTreeRay *ray, const float UNUSED(m_dist), const float *v0, const float *v1, const float *v2) { float dist; @@ -83,7 +83,7 @@ static float sphereray_tri_intersection(const BVHTreeRay *ray, float radius, con * Function adapted from David Eberly's distance tools (LGPL) * http://www.geometrictools.com/LibFoundation/Distance/Distance.html */ -static float nearest_point_in_tri_surface(const float *v0,const float *v1,const float *v2,const float *p, int *v, int *e, float *nearest ) +float nearest_point_in_tri_surface(const float *v0,const float *v1,const float *v2,const float *p, int *v, int *e, float *nearest ) { float diff[3]; float e0[3]; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c new file mode 100644 index 00000000000..9e9879fcae0 --- /dev/null +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -0,0 +1,3374 @@ +/** +***** 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. + * + * Contributor(s): Miika Hämäläinen + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "MEM_guardedalloc.h" + +#include +#include "stdio.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_kdtree.h" +#include "BLI_utildefines.h" + +#include "BKE_bvhutils.h" /* bvh tree */ +#include "BKE_blender.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_colortools.h" +#include "BKE_DerivedMesh.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_modifier.h" +#include "BKE_particle.h" +#include "BKE_report.h" +#include "BKE_texture.h" + +#include "DNA_dynamicpaint_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_userdef_types.h" /* to get temp file path */ + +/* for bake operator */ +#include "ED_screen.h" +#include "WM_types.h" +#include "WM_api.h" + +/* for image output */ +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" +#include "BKE_image.h" +#include "intern/IMB_filetype.h" +#ifdef WITH_OPENEXR +#include "intern/openexr/openexr_api.h" +#endif + +/* uv validate */ +#include "intern/MOD_util.h" + +/* Platform independend time */ +#include "PIL_time.h" + +/* to read object material color */ +#include "DNA_texture_types.h" +#include "../render/intern/include/render_types.h" +#include "../render/intern/include/voxeldata.h" +#include "DNA_material_types.h" +#include "RE_render_ext.h" + +#include "BKE_dynamicpaint.h" + + +#define DPOUTPUT_JPEG 0 +#define DPOUTPUT_PNG 1 +#define DPOUTPUT_OPENEXR 2 + +#define DPOUTPUT_PAINT 0 +#define DPOUTPUT_WET 1 +#define DPOUTPUT_DISPLACE 2 + +struct Object; +struct Scene; +struct DerivedMesh; +struct DynamicPaintModifierData; + +/* +* Init predefined antialias jitter data +*/ +float jitterDistances[5] = {0.0f, + 0.447213595f, + 0.447213595f, + 0.447213595f, + 0.5f}; + +/* precalculated gaussian factors for 5x super sampling */ +float gaussianFactors[5] = { 0.996849f, + 0.596145f, + 0.596145f, + 0.596145f, + 0.524141f}; +float gaussianTotal = 3.309425f; + +/* +* Neighbouring pixel table x and y list +*/ +int neighX[8] = {1,1,0,-1,-1,-1, 0, 1}; +int neighY[8] = {0,1,1, 1, 0,-1,-1,-1}; + + +/* +* Modifier call. Updates derived mesh data if baking. +*/ +void dynamicPaint_Modifier_update(struct DynamicPaintModifierData *pmd, DerivedMesh *dm) +{ + + if (!pmd->baking) return; + + if((pmd->type & MOD_DYNAMICPAINT_TYPE_CANVAS) && pmd->canvas) { + + if (pmd->canvas->dm) pmd->canvas->dm->release(pmd->canvas->dm); + pmd->canvas->dm = CDDM_copy(dm); + } + else if((pmd->type & MOD_DYNAMICPAINT_TYPE_PAINT) && pmd->paint) { + + if (pmd->paint->dm) pmd->paint->dm->release(pmd->paint->dm); + pmd->paint->dm = CDDM_copy(dm); + } + +} + +/* +* Free canvas data. +*/ +static void dynamicPaint_Modifier_freeCanvas(struct DynamicPaintModifierData *pmd) +{ + if(pmd->canvas) + { + if (pmd->canvas->dm) + pmd->canvas->dm->release(pmd->canvas->dm); + pmd->canvas->dm = NULL; + } +} + +/* +* Free paint data. +*/ +static void dynamicPaint_Modifier_freePaint(struct DynamicPaintModifierData *pmd) +{ + if(pmd->paint) + { + if(pmd->paint->dm) + pmd->paint->dm->release(pmd->paint->dm); + pmd->paint->dm = NULL; + + + if(pmd->paint->paint_ramp) + MEM_freeN(pmd->paint->paint_ramp); + pmd->paint->paint_ramp = NULL; + } +} + +/* +* Free whole dp modifier. +*/ +void dynamicPaint_Modifier_free(struct DynamicPaintModifierData *pmd) +{ + if(pmd) + { + dynamicPaint_Modifier_freeCanvas(pmd); + dynamicPaint_Modifier_freePaint(pmd); + } +} + +/* +* Initialize modifier data. +*/ +void dynamicPaint_Modifier_createType(struct DynamicPaintModifierData *pmd) +{ + if(pmd) + { + if(pmd->type & MOD_DYNAMICPAINT_TYPE_CANVAS) + { + if(pmd->canvas) + dynamicPaint_Modifier_freeCanvas(pmd); + + pmd->canvas = MEM_callocN(sizeof(DynamicPaintCanvasSettings), "DynamicPaintCanvas"); + pmd->canvas->pmd = pmd; + + pmd->canvas->flags = MOD_DPAINT_ANTIALIAS | MOD_DPAINT_MULALPHA | MOD_DPAINT_DRY_LOG; + pmd->canvas->output = MOD_DPAINT_OUT_PAINT; + pmd->canvas->effect = 0; + pmd->canvas->effect_ui = 1; + + pmd->canvas->diss_speed = 300; + pmd->canvas->dry_speed = 300; + pmd->canvas->dflat_speed = 300; + + pmd->canvas->disp_depth = 1.0f; + pmd->canvas->disp_type = MOD_DPAINT_DISP_DISPLACE; + pmd->canvas->disp_format = MOD_DPAINT_DISPFOR_PNG; + + pmd->canvas->resolution = 256; + pmd->canvas->start_frame = 1; + pmd->canvas->end_frame = 100; + pmd->canvas->substeps = 0; + + pmd->canvas->spread_speed = 1.0f; + pmd->canvas->drip_speed = 1.0f; + pmd->canvas->shrink_speed = 1.0f; + + sprintf(pmd->canvas->paint_output_path, "%spaintmap", "/tmp\\"); + sprintf(pmd->canvas->wet_output_path, "%swetmap", "/tmp\\"); + sprintf(pmd->canvas->displace_output_path, "%sdispmap", "/tmp\\"); + + pmd->canvas->ui_info[0] = '\0'; + + + pmd->canvas->dm = NULL; + + } + else if(pmd->type & MOD_DYNAMICPAINT_TYPE_PAINT) + { + if(pmd->paint) + dynamicPaint_Modifier_freePaint(pmd); + + pmd->paint = MEM_callocN(sizeof(DynamicPaintPainterSettings), "DynamicPaint Paint"); + pmd->paint->pmd = pmd; + + pmd->paint->psys = NULL; + + pmd->paint->flags = MOD_DPAINT_DO_PAINT | MOD_DPAINT_DO_WETNESS | MOD_DPAINT_DO_DISPLACE | MOD_DPAINT_ABS_ALPHA; + pmd->paint->collision = MOD_DPAINT_COL_VOLUME; + + pmd->paint->mat = NULL; + pmd->paint->r = 1.0f; + pmd->paint->g = 1.0f; + pmd->paint->b = 1.0f; + pmd->paint->alpha = 1.0f; + pmd->paint->wetness = 1.0f; + + pmd->paint->paint_distance = 0.1f; + pmd->paint->proximity_falloff = MOD_DPAINT_PRFALL_SHARP; + + pmd->paint->displace_distance = 0.5f; + pmd->paint->prox_displace_strength = 0.5f; + + pmd->paint->particle_radius = 0.2; + pmd->paint->particle_smooth = 0.05; + + pmd->paint->dm = NULL; + + /* + * Paint proximity falloff colorramp. + */ + { + CBData *ramp; + + pmd->paint->paint_ramp = add_colorband(0); + ramp = pmd->paint->paint_ramp->data; + /* Add default smooth-falloff ramp. */ + ramp[0].r = ramp[0].g = ramp[0].b = ramp[0].a = 1.0f; + ramp[0].pos = 0.0f; + ramp[1].r = ramp[1].g = ramp[1].b = ramp[1].pos = 1.0f; + ramp[1].a = 0.0f; + pmd->paint->paint_ramp->tot = 2; + } + } + } +} + +void dynamicPaint_Modifier_copy(struct DynamicPaintModifierData *pmd, struct DynamicPaintModifierData *tpmd) +{ + /* Init modifier */ + tpmd->type = pmd->type; + dynamicPaint_Modifier_createType(tpmd); + + /* Copy data */ + if (tpmd->canvas) { + pmd->canvas->pmd = tpmd; + + tpmd->canvas->flags = pmd->canvas->flags; + tpmd->canvas->output = pmd->canvas->output; + tpmd->canvas->disp_type = pmd->canvas->disp_type; + tpmd->canvas->disp_format = pmd->canvas->disp_format; + tpmd->canvas->effect = pmd->canvas->effect; + tpmd->canvas->effect_ui = 1; + + tpmd->canvas->resolution = pmd->canvas->resolution; + tpmd->canvas->start_frame = pmd->canvas->start_frame; + tpmd->canvas->end_frame = pmd->canvas->end_frame; + tpmd->canvas->substeps = pmd->canvas->substeps; + + tpmd->canvas->dry_speed = pmd->canvas->dry_speed; + tpmd->canvas->diss_speed = pmd->canvas->diss_speed; + tpmd->canvas->disp_depth = pmd->canvas->disp_depth; + tpmd->canvas->dflat_speed = pmd->canvas->dflat_speed; + + strncpy(tpmd->canvas->paint_output_path, pmd->canvas->paint_output_path, 240); + strncpy(tpmd->canvas->wet_output_path, pmd->canvas->wet_output_path, 240); + strncpy(tpmd->canvas->displace_output_path, pmd->canvas->displace_output_path, 240); + + tpmd->canvas->spread_speed = pmd->canvas->spread_speed; + tpmd->canvas->drip_speed = pmd->canvas->drip_speed; + tpmd->canvas->shrink_speed = pmd->canvas->shrink_speed; + + strncpy(tpmd->canvas->uvlayer_name, tpmd->canvas->uvlayer_name, 32); + + } else if (tpmd->paint) { + pmd->paint->pmd = tpmd; + + tpmd->paint->flags = pmd->paint->flags; + tpmd->paint->collision = pmd->paint->collision; + + tpmd->paint->r = pmd->paint->r; + tpmd->paint->g = pmd->paint->g; + tpmd->paint->b = pmd->paint->b; + tpmd->paint->alpha = pmd->paint->alpha; + tpmd->paint->wetness = pmd->paint->wetness; + + tpmd->paint->particle_radius = pmd->paint->particle_radius; + tpmd->paint->particle_smooth = pmd->paint->particle_smooth; + tpmd->paint->paint_distance = pmd->paint->paint_distance; + tpmd->paint->psys = pmd->paint->psys; + tpmd->paint->displace_distance = pmd->paint->displace_distance; + tpmd->paint->prox_displace_strength = pmd->paint->prox_displace_strength; + + tpmd->paint->paint_ramp = pmd->paint->paint_ramp; + + tpmd->paint->proximity_falloff = pmd->paint->proximity_falloff; + } +} + + + +void dynamicPaint_Modifier_do(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm) +{ + /* Update derived mesh data to modifier if baking */ + dynamicPaint_Modifier_update(pmd, dm); +} + + +/* +* Tries to find the neighbouring pixel in given (uv space) direction. +* Result is used by effect system to move paint on the surface. +* +* px,py : origin pixel x and y +* n_index : lookup direction index (use neighX,neighY to get final index) +*/ +static int dynamicPaint_findNeighbourPixel(DynamicPaintCanvasSettings *canvas, int px, int py, int n_index) +{ + /* Note: Current method only uses polygon edges to detect neighbouring pixels. + * -> It doesn't always lead to the optimum pixel but is accurate enough + * and faster/simplier than including possible face tip point links) + */ + + int x,y; + PaintSurfacePoint *tPoint = NULL; + PaintSurfacePoint *cPoint = NULL; + PaintSurface *surface = NULL; + + surface = canvas->surface; + + x = px + neighX[n_index]; + y = py + neighY[n_index]; + + if (x<0 || x>=surface->w) return -1; + if (y<0 || y>=surface->h) return -1; + + tPoint = (&surface->point[x+surface->w*y]); /* UV neighbour */ + + cPoint = (&surface->point[px+surface->w*py]); /* Origin point */ + + /* + * Check if target point is on same face -> mark it as neighbour + * (and if it isn't marked as an "edge pixel") + */ + if ((tPoint->index == cPoint->index) && (tPoint->neighbour_pixel == -1)) { + /* If it's on the same face, it has to be a correct neighbour */ + return (x+surface->w*y); + } + + + /* + * If the uv neighbour is mapped directly to + * a face -> use this point. + * + * !! Replace with "is uv faces linked" check !! + * This should work fine as long as uv island + * margin is > 1 pixel. + */ + if ((tPoint->index != -1) && (tPoint->neighbour_pixel == -1)) { + return (x+surface->w*y); + } + + /* + * If we get here, the actual neighbouring pixel + * points to a non-linked uv face, and we have to find + * it's "real" neighbour. + * + * Simple neighbouring face finding algorithm: + * - find closest uv edge to that pixel and get + * the other face connected to that edge on actual mesh + * - find corresponding position of that new face edge + * in uv space + * + * TODO: Implement something more accurate / optimized? + */ + { + int numOfFaces = canvas->dm->getNumFaces(canvas->dm); + MVert *mvert = NULL; + MFace *mface = NULL; + MTFace *tface = NULL; + + mvert = canvas->dm->getVertArray(canvas->dm); + mface = canvas->dm->getFaceArray(canvas->dm); + tface = DM_get_face_data_layer(canvas->dm, CD_MTFACE); + + /* Get closest edge to that subpixel on UV map */ + { + float pixel[2], dist, t_dist; + int i, uindex[2], edge1_index, edge2_index, e1_index, e2_index, target_face; + + float closest_point[2], lambda, dir_vec[2]; + int target_uv1, target_uv2, final_pixel[2], final_index; + + float (*s_uv1),(*s_uv2), (*t_uv1), (*t_uv2); + + pixel[0] = ((float)(px + neighX[n_index]) + 0.5f) / (float)surface->w; + pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)surface->h; + + /* Get uv indexes for current face part */ + if (cPoint->quad) { + uindex[0] = 0; uindex[1] = 2; uindex[2] = 3; + } + else { + uindex[0] = 0; uindex[1] = 1; uindex[2] = 2; + } + + /* + * Find closest edge to that pixel + */ + /* Dist to first edge */ + e1_index = cPoint->v1; e2_index = cPoint->v2; edge1_index = uindex[0]; edge2_index = uindex[1]; + dist = dist_to_line_segment_v2(pixel, tface[cPoint->index].uv[edge1_index], tface[cPoint->index].uv[edge2_index]); + + /* Dist to second edge */ + t_dist = dist_to_line_segment_v2(pixel, tface[cPoint->index].uv[uindex[1]], tface[cPoint->index].uv[uindex[2]]); + if (t_dist < dist) {e1_index = cPoint->v2; e2_index = cPoint->v3; edge1_index = uindex[1]; edge2_index = uindex[2]; dist = t_dist;} + + /* Dist to third edge */ + t_dist = dist_to_line_segment_v2(pixel, tface[cPoint->index].uv[uindex[2]], tface[cPoint->index].uv[uindex[0]]); + if (t_dist < dist) {e1_index = cPoint->v3; e2_index = cPoint->v1; edge1_index = uindex[2]; edge2_index = uindex[0]; dist = t_dist;} + + + /* + * Now find another face that is linked to that edge + */ + target_face = -1; + + for (i=0; iindex) continue; + + target_face = i; + + /* + * Get edge UV index + */ + if (e1_index == mface[i].v1) target_uv1 = 0; + else if (e1_index == mface[i].v2) target_uv1 = 1; + else if (e1_index == mface[i].v3) target_uv1 = 2; + else target_uv1 = 3; + + if (e2_index == mface[i].v1) target_uv2 = 0; + else if (e2_index == mface[i].v2) target_uv2 = 1; + else if (e2_index == mface[i].v3) target_uv2 = 2; + else target_uv2 = 3; + + break; + } + } + + /* If none found return -1 */ + if (target_face == -1) return -1; + + /* + * If target face is connected in UV space as well, just use original index + */ + s_uv1 = (float *)tface[cPoint->index].uv[edge1_index]; + s_uv2 = (float *)tface[cPoint->index].uv[edge2_index]; + t_uv1 = (float *)tface[target_face].uv[target_uv1]; + t_uv2 = (float *)tface[target_face].uv[target_uv2]; + + //printf("connected UV : %f,%f & %f,%f - %f,%f & %f,%f\n", s_uv1[0], s_uv1[1], s_uv2[0], s_uv2[1], t_uv1[0], t_uv1[1], t_uv2[0], t_uv2[1]); + + if (((s_uv1[0] == t_uv1[0] && s_uv1[1] == t_uv1[1]) && + (s_uv2[0] == t_uv2[0] && s_uv2[1] == t_uv2[1]) ) || + ((s_uv2[0] == t_uv1[0] && s_uv2[1] == t_uv1[1]) && + (s_uv1[0] == t_uv2[0] && s_uv1[1] == t_uv2[1]) )) return ((px+neighX[n_index]) + surface->w*(py+neighY[n_index])); + + /* + * Find a point that is relatively at same edge position + * on this other face UV + */ + lambda = closest_to_line_v2(closest_point, pixel, tface[cPoint->index].uv[edge1_index], tface[cPoint->index].uv[edge2_index]); + if (lambda < 0.0f) lambda = 0.0f; + if (lambda > 1.0f) lambda = 1.0f; + + sub_v2_v2v2(dir_vec, tface[target_face].uv[target_uv2], tface[target_face].uv[target_uv1]); + + mul_v2_fl(dir_vec, lambda); + + copy_v2_v2(pixel, tface[target_face].uv[target_uv1]); + add_v2_v2(pixel, dir_vec); + pixel[0] = (pixel[0] * (float)surface->w) - 0.5f; + pixel[1] = (pixel[1] * (float)surface->h) - 0.5f; + + final_pixel[0] = (int)floor(pixel[0]); + final_pixel[1] = (int)floor(pixel[1]); + + /* If current pixel uv is outside of texture */ + if (final_pixel[0] < 0 || final_pixel[0] >= surface->w) return -1; + if (final_pixel[1] < 0 || final_pixel[1] >= surface->h) return -1; + + final_index = final_pixel[0] + surface->w * final_pixel[1]; + + /* If we ended up to our origin point ( mesh has smaller than pixel sized faces) */ + if (final_index == (px+surface->w*py)) return -1; + /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ + if (surface->point[final_index].index != target_face) return -1; + + /* + * If final point is an "edge pixel", use it's "real" neighbour instead + */ + if (surface->point[final_index].neighbour_pixel != -1) final_index = cPoint->neighbour_pixel; + + return final_index; + } + } +} + +/* +* Output error message to both ui and console +*/ +static void dpError(DynamicPaintCanvasSettings *canvas, char *string) +{ + + if (strlen(string)>64) string[63] = '\0'; + + /* Add error to canvas ui info label */ + sprintf(canvas->error, string); + + /* Print console output */ + printf("DynamicPaint bake failed: %s\n", canvas->error); +} + +/* +* Create Canvas Surface for baking +*/ +static int dynamicPaint_createCanvasSurface(DynamicPaintCanvasSettings *canvas) +{ + + int yy; + int w,h; + + /* Antialias jitter point relative coords */ + float jitter5sample[10] = {0.0f, 0.0f, + -0.2f, -0.4f, + 0.2f, 0.4f, + 0.4f, -0.2f, + -0.4f, 0.3f}; + + DerivedMesh *dm = canvas->dm; + int numOfFaces; + MVert *mvert = NULL; + MFace *mface = NULL; + MTFace *tface = NULL; + + PaintSurface *surface = NULL; + BB2d *faceBB = NULL; + char uvname[32]; + + if (!dm) { dpError(canvas, "Canvas mesh not updated."); return 0;} + numOfFaces = dm->getNumFaces(dm); + + + /* Allocate memory for surface */ + canvas->surface = (struct PaintSurface *) MEM_callocN(sizeof(struct PaintSurface), "MPCanvasSurface"); + if (canvas->surface == NULL) {dpError(canvas, "Not enough free memory."); return 0;} + + surface = canvas->surface; + surface->point = NULL; + + mvert = dm->getVertArray(dm); + mface = dm->getFaceArray(dm); + + validate_layer_name(&dm->faceData, CD_MTFACE, canvas->uvlayer_name, uvname); + tface = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, uvname); + + /* Check for validity */ + if (!tface) {dpError(canvas, "No UV data on canvas."); return 0;} + if (canvas->resolution < 16 || canvas->resolution > 8096) {dpError(canvas, "Invalid resolution."); return 0;} + + w = h = canvas->resolution; + surface->w = w; + surface->h = h; + + /* + * Start generating the surface + */ + printf("DynamicPaint: Preparing canvas of %ix%i pixels and %i faces.\n", w, h, numOfFaces); + + surface->pixelSamples = (canvas->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + surface->point = (struct PaintSurfacePoint *) MEM_callocN(w*h*sizeof(struct PaintSurfacePoint), "PaintSurfaceData"); + if (surface->point == NULL) {dpError(canvas, "Not enough free memory."); return 0;} + + /* + * Generate a temporary bounding box array for UV faces to optimize + * the pixel-inside-a-face search. + */ + faceBB = (struct BB2d *) MEM_mallocN(numOfFaces*sizeof(struct BB2d), "MPCanvasFaceBB"); + if (faceBB == NULL) {dpError(canvas, "Not enough free memory."); return 0;} + + for (yy=0; yy faceBB[yy].max[0]) faceBB[yy].max[0] = tface[yy].uv[i][0]; + if (tface[yy].uv[i][1] > faceBB[yy].max[1]) faceBB[yy].max[1] = tface[yy].uv[i][1]; + + } + } // end face loop + + /* + * Allocate antialias sample data (without threads due to malloc) + * (Non threadable?) + */ + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int index = xx+w*yy; + PaintSurfacePoint *cPoint = (&surface->point[index]); + + /* Initialize barycentricWeights */ + cPoint->barycentricWeights = (struct Vec3f *) malloc( surface->pixelSamples * sizeof(struct Vec3f )); + if (cPoint->barycentricWeights == NULL) {dpError(canvas, "Not enough free memory."); return 0;} + + } + } // end pixel loop + + /* + * Loop through every pixel and check + * if pixel is uv-mapped on a canvas face. + */ + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int i, sample; + int index = xx+w*yy; + PaintSurfacePoint *cPoint = (&surface->point[index]); + + short isInside = 0; /* if point is inside a uv face */ + + float d1[2], d2[2], d3[2], point[5][2]; + float dot00,dot01,dot02,dot11,dot12, invDenom, u,v; + + /* + * Init per pixel settings + */ + cPoint->color[0] = 0.0f; + cPoint->color[1] = 0.0f; + cPoint->color[2] = 0.0f; + cPoint->alpha = 0.0f; + cPoint->depth = 0.0f; + + cPoint->wetness = 0.0f; + cPoint->e_alpha = 0.0f; + cPoint->e_color[0] = 0.0f; + cPoint->e_color[1] = 0.0f; + cPoint->e_color[2] = 0.0f; + cPoint->state = 0; + + cPoint->index = -1; + cPoint->neighbour_pixel = -1; + + /* Actual pixel center, used when collision is found */ + point[0][0] = ((float)xx + 0.5f) / w; + point[0][1] = ((float)yy + 0.5f) / h; + + /* + * A pixel middle sample isn't enough to find very narrow polygons + * So using 4 samples of each corner too + */ + point[1][0] = ((float)xx) / w; + point[1][1] = ((float)yy) / h; + + point[2][0] = ((float)xx+1) / w; + point[2][1] = ((float)yy) / h; + + point[3][0] = ((float)xx) / w; + point[3][1] = ((float)yy+1) / h; + + point[4][0] = ((float)xx+1) / w; + point[4][1] = ((float)yy+1) / h; + + + /* Loop through pixel samples, starting from middle point */ + for (sample=0; sample<5; sample++) { + + /* Loop through every face in the mesh */ + for (i=0; i (point[sample][0])) continue; + if (faceBB[i].min[1] > (point[sample][1])) continue; + if (faceBB[i].max[0] < (point[sample][0])) continue; + if (faceBB[i].max[1] < (point[sample][1])) continue; + + /* + * Calculate point inside a triangle check + * for uv0,1,2 + */ + VECSUB2D(d1, tface[i].uv[2], tface[i].uv[0]); // uv2 - uv0 + VECSUB2D(d2, tface[i].uv[1], tface[i].uv[0]); // uv1 - uv0 + VECSUB2D(d3, point[sample], tface[i].uv[0]); // point - uv0 + + + dot00 = d1[0]*d1[0] + d1[1]*d1[1]; + dot01 = d1[0]*d2[0] + d1[1]*d2[1]; + dot02 = d1[0]*d3[0] + d1[1]*d3[1]; + dot11 = d2[0]*d2[0] + d2[1]*d2[1]; + dot12 = d2[0]*d3[0] + d2[1]*d3[1]; + + invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + u = (dot11 * dot02 - dot01 * dot12) * invDenom; + v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + if ((u > 0) && (v > 0) && (u + v < 1)) {isInside=1;} /* is inside a triangle */ + + /* + * If collision wasn't found but the face is a quad + * do another check for the second half + */ + if ((!isInside) && mface[i].v4) + { + + /* change d2 to test the other half */ + VECSUB2D(d2, tface[i].uv[3], tface[i].uv[0]); // uv3 - uv0 + + /* test again */ + dot00 = d1[0]*d1[0] + d1[1]*d1[1]; + dot01 = d1[0]*d2[0] + d1[1]*d2[1]; + dot02 = d1[0]*d3[0] + d1[1]*d3[1]; + dot11 = d2[0]*d2[0] + d2[1]*d2[1]; + dot12 = d2[0]*d3[0] + d2[1]*d3[1]; + + invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + u = (dot11 * dot02 - dot01 * dot12) * invDenom; + v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + if ((u > 0) && (v > 0) && (u + v < 1)) {isInside=2;} /* is inside the second half of the quad */ + + } + + /* + * If point was inside the face + */ + if (isInside != 0) { + + float uv1co[2], uv2co[2], uv3co[2], uv[2]; + int j; + + /* Get triagnle uvs */ + if (isInside==1) { + VECCOPY2D(uv1co, tface[i].uv[0]); + VECCOPY2D(uv2co, tface[i].uv[1]); + VECCOPY2D(uv3co, tface[i].uv[2]); + } + else { + VECCOPY2D(uv1co, tface[i].uv[0]); + VECCOPY2D(uv2co, tface[i].uv[2]); + VECCOPY2D(uv3co, tface[i].uv[3]); + } + + /* Add b-weights per anti-aliasing sample */ + for (j=0; jpixelSamples; j++) { + + uv[0] = point[0][0] + jitter5sample[j*2] / w; + uv[1] = point[0][1] + jitter5sample[j*2+1] / h; + + barycentric_weights_v2(uv1co, uv2co, uv3co, uv, cPoint->barycentricWeights[j].v); + } + + /* Set surface point face values */ + cPoint->index = i; /* face index */ + cPoint->quad = (isInside == 2) ? 1 : 0; /* quad or tri part*/ + + /* save vertex indexes */ + cPoint->v1 = (isInside == 2) ? mface[i].v1 : mface[i].v1; + cPoint->v2 = (isInside == 2) ? mface[i].v3 : mface[i].v2; + cPoint->v3 = (isInside == 2) ? mface[i].v4 : mface[i].v3; + + sample = 5; /* make sure we exit sample loop as well */ + break; + } // end isInside + } // end face loop + } // end sample + } // end of yy loop + } // end of xx loop + + + + /* + * Now loop through every pixel that was left without index + * and find if they have neighbouring pixels that have an index. + * If so use that polygon as pixel surface. + * (To avoid seams on uv island edges.) + */ + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int index = xx+w*yy; + PaintSurfacePoint *cPoint = (&surface->point[index]); + + /* If point isnt't on canvas mesh */ + if (cPoint->index == -1) { + int u_min, u_max, v_min, v_max; + int u,v, ind; + float point[2]; + + /* get loop area */ + u_min = (xx > 0) ? -1 : 0; + u_max = (xx < (w-1)) ? 1 : 0; + v_min = (yy > 0) ? -1 : 0; + v_max = (yy < (h-1)) ? 1 : 0; + + point[0] = ((float)xx + 0.5f) / w; + point[1] = ((float)yy + 0.5f) / h; + + /* search through defined area for neighbour */ + for (u=u_min; u<=u_max; u++) + for (v=v_min; v<=v_max; v++) { + + /* if not this pixel itself */ + if (u!=0 || v!=0) { + ind = (xx+u)+w*(yy+v); + + /* if neighbour has index */ + if (surface->point[ind].index != -1) { + + float uv1co[2], uv2co[2], uv3co[2], uv[2]; + int i = surface->point[ind].index, j; + + /* + * Now calculate pixel data for this pixel as it was on polygon surface + */ + if (!surface->point[ind].quad) { + VECCOPY2D(uv1co, tface[i].uv[0]); + VECCOPY2D(uv2co, tface[i].uv[1]); + VECCOPY2D(uv3co, tface[i].uv[2]); + } + else { + VECCOPY2D(uv1co, tface[i].uv[0]); + VECCOPY2D(uv2co, tface[i].uv[2]); + VECCOPY2D(uv3co, tface[i].uv[3]); + } + + /* Add b-weights per anti-aliasing sample */ + for (j=0; jpixelSamples; j++) { + + uv[0] = point[0] + jitter5sample[j*2] / w; + uv[1] = point[1] + jitter5sample[j*2+1] / h; + barycentric_weights_v2(uv1co, uv2co, uv3co, uv, cPoint->barycentricWeights[j].v); + } + + /* Set values */ + cPoint->neighbour_pixel = ind; // face index + cPoint->quad = surface->point[ind].quad; // quad or tri + + /* save vertex indexes */ + cPoint->v1 = (cPoint->quad) ? mface[i].v1 : mface[i].v1; + cPoint->v2 = (cPoint->quad) ? mface[i].v3 : mface[i].v2; + cPoint->v3 = (cPoint->quad) ? mface[i].v4 : mface[i].v3; + + u = u_max + 1; /* make sure we exit outer loop as well */ + break; + } + + } // end itself check + } // end uv loop + } // end if has index + } + } // end pixel loop + + /* + * When base loop is over convert found neighbour indexes to real ones + * Also count the final number of active surface points + */ + surface->active_points = 0; + + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int index = xx+w*yy; + PaintSurfacePoint *cPoint = (&surface->point[index]); + + if (cPoint->index == -1 && cPoint->neighbour_pixel != -1) cPoint->index = surface->point[cPoint->neighbour_pixel].index; + if (cPoint->index != -1) surface->active_points++; + } + } // end pixel loop + +#if 0 + /* + * ----------------------------------------------------------------- + * For debug, output pixel statuses to the color map + * ----------------------------------------------------------------- + */ + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int index = xx+w*yy; + PaintSurfacePoint *cPoint = (&surface->point[index]); + cPoint->alpha=1.0f; + + /* Every pixel that is assigned as "edge pixel" gets blue color */ + if (cPoint->neighbour_pixel != -1) cPoint->color[2] = 1.0f; + /* and every pixel that finally got an polygon gets red color */ + if (cPoint->index != -1) cPoint->color[0] = 1.0f; + /* green color shows pixel face index hash */ + if (cPoint->index != -1) cPoint->color[1] = (float)(cPoint->index % 255)/256.0f; + } + } // end pixel loop + +#endif + + /* + * If any effect enabled, create surface effect / wet layer + * neighbour lists. Processes possibly moving data. + */ + if (canvas->effect) { + + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int i; + PaintSurfacePoint *cPoint = (&surface->point[xx+w*yy]); + + /* If current point exists find all it's neighbouring pixels */ + if (cPoint->index != -1) + for (i=0; i<8; i++) { + + /* Try to find a neighbouring pixel in defined direction + * If not found, -1 is returned */ + cPoint->neighbours[i] = dynamicPaint_findNeighbourPixel(canvas, xx, yy, i); + } + } + } // end pixel loop + } // effect + + MEM_freeN(faceBB); + + return 1; +} + + +/* +* Free canvas surface +*/ +static void dynamicPaint_cleanCanvasSurface(DynamicPaintCanvasSettings *canvas) +{ + + int w,h,k; + + if (!canvas) return; + if (!canvas->surface) return; + + w = canvas->surface->w; + h = canvas->surface->h; + + #pragma omp parallel for schedule(static,1) + for (k = 0; k < w*h; k++) + { + free(canvas->surface->point[k].barycentricWeights); + } + + if (canvas->surface->point) MEM_freeN(canvas->surface->point); + MEM_freeN(canvas->surface); +} + +/* A modified callback to bvh tree raycast. The tree must bust have been built using bvhtree_from_mesh_faces. +* userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. +* +* To optimize paint detection speed this doesn't calculate hit coordinates or normal. +* If ray hit the second half of a quad, no[0] is set to 1.0f. +*/ +static void mesh_faces_spherecast_dp(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + const BVHTreeFromMesh *data = (BVHTreeFromMesh*) userdata; + MVert *vert = data->vert; + MFace *face = data->face + index; + short quad = 0; + + float *t0, *t1, *t2, *t3; + t0 = vert[ face->v1 ].co; + t1 = vert[ face->v2 ].co; + t2 = vert[ face->v3 ].co; + t3 = face->v4 ? vert[ face->v4].co : NULL; + + do + { + float dist; + dist = ray_tri_intersection(ray, hit->dist, t0, t1, t2); + + if(dist >= 0 && dist < hit->dist) + { + hit->index = index; + hit->dist = dist; + //VECADDFAC(hit->co, ray->origin, ray->direction, dist); + //normal_tri_v3( hit->no,t0, t1, t2); + hit->no[0] = (quad) ? 1.0f : 0.0f; + } + + t1 = t2; + t2 = t3; + t3 = NULL; + quad = 1; + + } while(t2); +} + +/* A modified callback to bvh tree nearest point. The tree must bust have been built using bvhtree_from_mesh_faces. +* userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. +* +* To optimize paint detection speed this doesn't calculate hit normal. +* If ray hit the second half of a quad, no[0] is set to 1.0f, else 0.0f +*/ +static void mesh_faces_nearest_point_dp(void *userdata, int index, const float *co, BVHTreeNearest *nearest) +{ + const BVHTreeFromMesh *data = (BVHTreeFromMesh*) userdata; + MVert *vert = data->vert; + MFace *face = data->face + index; + short quad = 0; + + float *t0, *t1, *t2, *t3; + t0 = vert[ face->v1 ].co; + t1 = vert[ face->v2 ].co; + t2 = vert[ face->v3 ].co; + t3 = face->v4 ? vert[ face->v4].co : NULL; + + do + { + float nearest_tmp[3], dist; + int vertex, edge; + + dist = nearest_point_in_tri_surface(t0, t1, t2, co, &vertex, &edge, nearest_tmp); + if(dist < nearest->dist) + { + nearest->index = index; + nearest->dist = dist; + VECCOPY(nearest->co, nearest_tmp); + //normal_tri_v3( nearest->no,t0, t1, t2); + nearest->no[0] = (quad) ? 1.0f : 0.0f; + } + + t1 = t2; + t2 = t3; + t3 = NULL; + quad = 1; + + } while(t2); +} + + +/* +* Calculate inverse matrices for material related objects +* in case texture is mapped to an object. +* (obj->imat isn't auto-updated) +*/ +static void DynamicPaint_UpdateMaterial(Material *mat) +{ + MTex *mtex = NULL; + Tex *tex = NULL; + int tex_nr; + + if (mat == NULL) return; + + /* + * Loop through every material texture and check + * if they are mapped by other object + */ + + for(tex_nr=0; tex_nrseptex & (1<mtex[tex_nr]) { + mtex= mat->mtex[tex_nr]; + tex= mtex->tex; + + if(tex==0) continue; + + /* which coords */ + if(mtex->texco==TEXCO_OBJECT) { + Object *ob= mtex->object; + if(ob) { + invert_m4_m4(ob->imat, ob->obmat); + } + } + + } + } // end texture loop + +} + + +static void DynamicPaint_InitMaterialObjects(Object *paintOb, Material *ui_mat) +{ + + /* + * Calculate inverse transformation matrix + * for this object + */ + invert_m4_m4(paintOb->imat, paintOb->obmat); + + /* Now process every material linked to this Paint object, + * and check if any material uses object mapping. */ + if ((ui_mat == NULL) && paintOb->totcol) { + /* If using materials that are linked to mesh, + * check every material linked to this object*/ + int i; + + for (i=0; itotcol; i++) { + if (paintOb->matbits[i]) DynamicPaint_UpdateMaterial(paintOb->mat[i]); + } + } + else { + DynamicPaint_UpdateMaterial(ui_mat); + } + +} + + + +/* a modified part of shadeinput.c -> shade_input_set_uv() / shade_input_set_shade_texco() */ +static void textured_face_generate_uv(float *uv, float *normal, float *hit, float *v1, float *v2, float *v3) +{ + + float detsh, t00, t10, t01, t11, xn, yn, zn; + int axis1, axis2; + + /* find most stable axis to project */ + xn= fabs(normal[0]); + yn= fabs(normal[1]); + zn= fabs(normal[2]); + + if(zn>=xn && zn>=yn) { axis1= 0; axis2= 1; } + else if(yn>=xn && yn>=zn) { axis1= 0; axis2= 2; } + else { axis1= 1; axis2= 2; } + + /* compute u,v and derivatives */ + t00= v3[axis1]-v1[axis1]; t01= v3[axis2]-v1[axis2]; + t10= v3[axis1]-v2[axis1]; t11= v3[axis2]-v2[axis2]; + + detsh= 1.0f/(t00*t11-t10*t01); + t00*= detsh; t01*=detsh; + t10*=detsh; t11*=detsh; + + uv[0] = (hit[axis1]-v3[axis1])*t11-(hit[axis2]-v3[axis2])*t10; + uv[1] = (hit[axis2]-v3[axis2])*t00-(hit[axis1]-v3[axis1])*t01; + + /* u and v are in range -1 to 0, we allow a little bit extra but not too much, screws up speedvectors */ + CLAMP(uv[0], -2.0f, 1.0f); + CLAMP(uv[1], -2.0f, 1.0f); +} + +/* a modified part of shadeinput.c -> shade_input_set_uv() / shade_input_set_shade_texco() */ +static void textured_face_get_uv(float *uv_co, float *normal, float *uv, int faceIndex, short quad, MTFace *tface) +{ + float *uv1, *uv2, *uv3; + float l; + + l= 1.0f+uv[0]+uv[1]; + + uv1= tface[faceIndex].uv[0]; + uv2= (quad) ? tface[faceIndex].uv[2] : tface[faceIndex].uv[1]; + uv3= (quad) ? tface[faceIndex].uv[3] : tface[faceIndex].uv[2]; + + uv_co[0]= -1.0f + 2.0f*(l*uv3[0]-uv[0]*uv1[0]-uv[1]*uv2[0]); + uv_co[1]= -1.0f + 2.0f*(l*uv3[1]-uv[0]*uv1[1]-uv[1]*uv2[1]); + uv_co[2]= 0.0f; /* texture.c assumes there are 3 coords */ +} + +/* +* Edited version of do_material_tex() +* +* Samples color and alpha from a "Surface" type material +* on a given point, without need for ShadeInput. +* +* Keep up-to-date with new mapping settings +* +* also see shade_input_set_shade_texco() for ORCO settings +* and shade_input_set_uv() for face uv calculation +*/ +void DynamicPaint_SampleSolidMaterial(float color[3], float *alpha, Material *mat, Object *paintOb, float xyz[3], int faceIndex, short isQuad, DerivedMesh *orcoDm) +{ + MTex *mtex = NULL; + Tex *tex = NULL; + TexResult texres= {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL}; + float co[3], xyz_local[3]; + float fact, stencilTin=1.0; + float texvec[3]; + int tex_nr, rgbnor= 0; + float uv[3], normal[3]; + MFace *mface; + int v1, v2, v3; + MVert *mvert; + + /* Get face data */ + mvert = orcoDm->getVertArray(orcoDm); + mface = orcoDm->getFaceArray(orcoDm); + v1=mface[faceIndex].v1, v2=mface[faceIndex].v2, v3=mface[faceIndex].v3; + if (isQuad) {v2=mface[faceIndex].v3; v3=mface[faceIndex].v4;} + + normal_tri_v3( normal, mvert[v1].co, mvert[v2].co, mvert[v3].co); + + /* Assign material base values */ + color[0] = mat->r; + color[1] = mat->g; + color[2] = mat->b; + *alpha = mat->alpha; + + VECCOPY(xyz_local, xyz); + mul_m4_v3(paintOb->imat, xyz_local); + + for(tex_nr=0; tex_nrseptex & (1<mtex[tex_nr]) { + mtex= mat->mtex[tex_nr]; + tex= mtex->tex; + + tex= mtex->tex; + if(tex==0) continue; + + /* which coords */ + if(mtex->texco==TEXCO_ORCO) { + float l; + + /* + * Get generated UV + */ + textured_face_generate_uv(uv, normal, xyz_local, mvert[v1].co, mvert[v2].co, mvert[v3].co); + + l= 1.0f+uv[0]+uv[1]; + + /* calculate generated coordinate + * ** Keep up-to-date with shadeinput.c -> shade_input_set_shade_texco() **/ + co[0]= l*mvert[v3].co[0]-uv[0]*mvert[v1].co[0]-uv[1]*mvert[v2].co[0]; + co[1]= l*mvert[v3].co[1]-uv[0]*mvert[v1].co[1]-uv[1]*mvert[v2].co[1]; + co[2]= l*mvert[v3].co[2]-uv[0]*mvert[v1].co[2]-uv[1]*mvert[v2].co[2]; + } + else if(mtex->texco==TEXCO_OBJECT) { + Object *ob= mtex->object; + + VECCOPY(co, xyz); + /* convert from world space to paint space */ + mul_m4_v3(paintOb->imat, co); + if(ob) { + mul_m4_v3(ob->imat, co); + } + } + else if(mtex->texco==TEXCO_GLOB) { + VECCOPY(co, xyz); + } + else if(mtex->texco==TEXCO_UV) { + MTFace *tface; + + /* Get UV layer */ + if(mtex->uvname[0] != 0) { + tface = CustomData_get_layer_named(&orcoDm->faceData, CD_MTFACE, mtex->uvname); + } + else tface = DM_get_face_data_layer(orcoDm, CD_MTFACE); + + /* Get generated coordinates to calculate UV from */ + textured_face_generate_uv(uv, normal, xyz_local, mvert[v1].co, mvert[v2].co, mvert[v3].co); + + /* Get UV mapping coordinate */ + textured_face_get_uv(co, normal, uv, faceIndex, isQuad, tface); + } + else continue; /* non-supported types get just skipped: + TEXCO_REFL, TEXCO_NORM, TEXCO_TANGENT + TEXCO_WINDOW, TEXCO_STRAND, TEXCO_STRESS etc. + */ + + /* get texture mapping */ + texco_mapping_ext(normal, tex, mtex, co, 0, 0, texvec); + + if(tex->use_nodes && tex->nodetree) { + /* No support for nodes (yet). */ + continue; + } + else { + rgbnor = multitex_ext(mtex->tex, co, 0, 0, 0, &texres); + } + + /* 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; + } + + /* Negate and stencil masks */ + 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; + } + } + + /* mapping */ + if(mtex->mapto & (MAP_COL)) { + float tcol[3]; + + /* stencil maps on the texture control slider, not texture intensity value */ + tcol[0]=texres.tr; tcol[1]=texres.tg; tcol[2]=texres.tb; + + if((rgbnor & TEX_RGB)==0) { + tcol[0]= mtex->r; + tcol[1]= mtex->g; + tcol[2]= mtex->b; + } + else if(mtex->mapto & MAP_ALPHA) { + texres.tin= stencilTin; + } + else texres.tin= texres.ta; + + if(mtex->mapto & MAP_COL) { + float colfac= mtex->colfac*stencilTin; + texture_rgb_blend(color, tcol, color, texres.tin, colfac, mtex->blendtype); + } + } + + if(mtex->mapto & MAP_VARS) { + /* stencil maps on the texture control slider, not texture intensity value */ + + 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(mtex->mapto & MAP_ALPHA) { + float alphafac= mtex->alphafac*stencilTin; + + *alpha= texture_value_blend(mtex->def_var, *alpha, texres.tin, alphafac, mtex->blendtype); + if(*alpha<0.0) *alpha= 0.0; + else if(*alpha>1.0) *alpha= 1.0; + } + } + } + } +} + + +/* +* Edited version of texture.c -> do_volume_tex() +* +* Samples color and density from a volume type texture +* without need for ShadeInput. +* +* Keep up-to-date with new mapping settings +*/ +void DynamicPaint_SampleVolumeMaterial(float color[3], float *alpha, Material *mat, Object *paintOb, float xyz[3]) +{ + + int mapto_flag = MAP_DENSITY | MAP_REFLECTION_COL | MAP_TRANSMISSION_COL; + float *col = color; + + MTex *mtex = NULL; + Tex *tex = NULL; + 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; + + /* set base color */ + color[0] = mat->vol.reflection_col[0]; + color[1] = mat->vol.reflection_col[1]; + color[2] = mat->vol.reflection_col[2]; + *alpha = mat->vol.density; + + for(tex_nr=0; tex_nrseptex & (1<mtex[tex_nr]) { + mtex= 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; + texres.nor= NULL; + + /* which coords */ + if(mtex->texco==TEXCO_OBJECT) { + Object *ob= mtex->object; + ob= mtex->object; + if(ob) { + VECCOPY(co, xyz); + mul_m4_v3(ob->imat, co); + } + } + else if(mtex->texco==TEXCO_ORCO) { + + { + Object *ob= paintOb; + VECCOPY(co, xyz); + mul_m4_v3(ob->imat, co); + } + } + else if(mtex->texco==TEXCO_GLOB) { + VECCOPY(co, xyz); + } + else continue; /* Skip unsupported types */ + + + if(tex->type==TEX_IMAGE) { + continue; /* not supported yet */ + } + 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_ext(tex, texvec, NULL, NULL, 0, &texres); + + /* 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; + } + + /* Negate and stencil */ + 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; + } + } + + /* Map values */ + if((mapto_flag & (MAP_EMISSION_COL+MAP_TRANSMISSION_COL+MAP_REFLECTION_COL)) && (mtex->mapto & (MAP_EMISSION_COL+MAP_TRANSMISSION_COL+MAP_REFLECTION_COL))) { + float tcol[3]; + + /* stencil maps on the texture control slider, not texture intensity value */ + + 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; + } + + /* used for emit */ + if((mapto_flag & MAP_EMISSION_COL) && (mtex->mapto & MAP_EMISSION_COL)) { + float colemitfac= mtex->colemitfac*stencilTin; + texture_rgb_blend(col, tcol, col, texres.tin, colemitfac, mtex->blendtype); + } + + if((mapto_flag & MAP_REFLECTION_COL) && (mtex->mapto & MAP_REFLECTION_COL)) { + float colreflfac= mtex->colreflfac*stencilTin; + texture_rgb_blend(col, tcol, col, texres.tin, colreflfac, mtex->blendtype); + } + + if((mapto_flag & MAP_TRANSMISSION_COL) && (mtex->mapto & MAP_TRANSMISSION_COL)) { + float coltransfac= mtex->coltransfac*stencilTin; + texture_rgb_blend(col, tcol, col, texres.tin, coltransfac, mtex->blendtype); + } + } + + if((mapto_flag & MAP_VARS) && (mtex->mapto & MAP_VARS)) { + /* stencil maps on the texture control slider, not texture intensity value */ + + /* 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_DENSITY) && (mtex->mapto & MAP_DENSITY)) { + float densfac= mtex->densfac*stencilTin; + + *alpha = texture_value_blend(mtex->def_var, *alpha, texres.tin, densfac, mtex->blendtype); + CLAMP(*alpha, 0.0, 1.0); + } + } + } + } +} + +/* +* Get material (including linked textures) diffuse color and alpha in given coordinates +* +* color,paint : input/output color values +* pixelCoord : canvas pixel coordinates in global space. used if material is volumetric +* +* paintHit : point on paint object surface in global space. used by "surface" type materials +* faceIndex : paintHit face index +* orcoDm : orco state derived mesh of paint object +* ui_mat : force material. if NULL, material linked to paintOb mesh is used. +* +* *"paint object" = object to sample material color from +*/ +void DynamicPaint_GetMaterialColor(float *color, float *alpha, Object *paintOb, float pixelCoord[3], float paintHit[3], int faceIndex, short isQuad, DerivedMesh *orcoDm, Material *ui_mat) +{ + + Material *material = ui_mat; + + /* + * Get face material + */ + if (material == NULL) { + MFace *mface = NULL; + mface = orcoDm->getFaceArray(orcoDm); + material = give_current_material(paintOb, mface[faceIndex].mat_nr+1); + + if (material == NULL) return; /* No material assigned */ + } + + /* + * Sample textured material color in given position depending on material type + */ + if (material->material_type == MA_TYPE_SURFACE) { + /* Solid material */ + DynamicPaint_SampleSolidMaterial(color, alpha, material, paintOb, paintHit, faceIndex, isQuad, orcoDm); + } + else if (material->material_type == MA_TYPE_VOLUME) { + /* Volumetric material */ + DynamicPaint_SampleVolumeMaterial(color, alpha, material, paintOb, pixelCoord); + } + else if (material->material_type == MA_TYPE_HALO) { + /* Halo type not supported */ + } +} + + +/* +* Mix color values to canvas point. +* +* cPoint : canvas surface point to do the changes +* paintFlags : paint object flags +* paintColor,Alpha,Wetness : to be mixed paint values +* +* timescale : value used to adjust time dependand +* operations when using substeps +*/ +void DynamicPaint_MixPaintColors(PaintSurfacePoint *cPoint, int paintFlags, float *paintColor, float *paintAlpha, float *paintWetness, float *timescale) +{ + + /* Add paint */ + if (!(paintFlags & MOD_DPAINT_ERASE)) { + float wetness; + + /* If point has previous paint */ + if (cPoint->e_alpha > 0) + { + /* + * Mix colors by the factor, use timescale + */ + float factor = (*paintAlpha) * (*timescale); + float invFact = 1.0f - factor; + cPoint->e_color[0] = cPoint->e_color[0]*invFact + paintColor[0]*factor; + cPoint->e_color[1] = cPoint->e_color[1]*invFact + paintColor[1]*factor; + cPoint->e_color[2] = cPoint->e_color[2]*invFact + paintColor[2]*factor; + } + else + { + /* else set first color value straight to paint color */ + cPoint->e_color[0] = paintColor[0]; + cPoint->e_color[1] = paintColor[1]; + cPoint->e_color[2] = paintColor[2]; + } + + /* alpha */ + if (paintFlags & MOD_DPAINT_ABS_ALPHA) { + if (cPoint->e_alpha < (*paintAlpha)) cPoint->e_alpha = (*paintAlpha); + } + else { + cPoint->e_alpha += (*paintAlpha) * (*timescale); + if (cPoint->e_alpha > 1.0f) cPoint->e_alpha = 1.0f; + } + + /* only increase wetness if it's below paint level */ + wetness = (*paintWetness) * cPoint->e_alpha; + if (cPoint->wetness < wetness) cPoint->wetness = wetness; + } + /* Erase paint */ + else { + float a_ratio, a_highest; + float wetness; + float invFact = 1.0f - (*paintAlpha); + + /* + * Make highest alpha to match erased value + * but maintain alpha ratio + */ + if (paintFlags & MOD_DPAINT_ABS_ALPHA) { + a_highest = (cPoint->e_alpha > cPoint->alpha) ? cPoint->e_alpha : cPoint->alpha; + if (a_highest > invFact) { + a_ratio = invFact / a_highest; + + cPoint->e_alpha *= a_ratio; + cPoint->alpha *= a_ratio; + } + } + else { + cPoint->e_alpha -= (*paintAlpha) * (*timescale); + if (cPoint->e_alpha < 0.0f) cPoint->e_alpha = 0.0f; + cPoint->alpha -= (*paintAlpha) * (*timescale); + if (cPoint->alpha < 0.0f) cPoint->alpha = 0.0f; + } + + wetness = (1.0f - (*paintWetness)) * cPoint->e_alpha; + if (cPoint->wetness > wetness) cPoint->wetness = wetness; + } +} + + +/* +* Paint an object mesh to canvas +*/ +static int DynamicPaint_PaintMesh(DynamicPaintCanvasSettings *canvas, Vec3f *canvasVerts, DynamicPaintPainterSettings *paint, Object *canvasOb, Object *paintOb, float timescale) +{ + DerivedMesh *dm = NULL; + MVert *mvert = NULL; + MFace *mface = NULL; + PaintSurface *surface = canvas->surface; + + if (!paint->dm) { + printf("DynamicPaint: Invalid paint dm.\n"); + return 0; + } + + /* If using material color, we prepare required stuff on texture related objects first */ + if (paint->flags & MOD_DPAINT_USE_MATERIAL) DynamicPaint_InitMaterialObjects(paintOb, paint->mat); + + { + BVHTreeFromMesh treeData = {0}; + int yy; + int tWidth = surface->w; + int tHeight = surface->h; + + int numOfVerts; + int ii; + + /* + * Transform collider vertices to world space + * (Faster than transforming per pixel + * coordinates and normals to object space) + */ + dm = CDDM_copy(paint->dm); + mvert = dm->getVertArray(dm); + mface = dm->getFaceArray(dm); + numOfVerts = dm->getNumVerts(dm); + + for (ii=0; iiobmat, mvert[ii].co); + } + + /* Build a bvh tree from transformed vertices */ + bvhtree_from_mesh_faces(&treeData, dm, 0.0f, 4, 6); + + if(treeData.tree) { + #pragma omp parallel for schedule(static) + for (yy = 0; yy < tHeight; yy++) + { + int xx,i; + for (xx = 0; xx < tWidth; xx++) + { + PaintSurfacePoint *cPoint = (&surface->point[xx+tWidth*yy]); + i = cPoint->index; + + if (i >= 0) + { + int ss; + float ssFactor = 0.0f; /* super-sampling factor */ + float depth = 0.0f; /* displace depth */ + + float paintColor[3] = {0.0f, 0.0f, 0.0f}; + int numOfHits = 0; + float paintAlpha = 0.0f; + + /* Supersampling */ + for (ss=0; sspixelSamples; ss++) { + + float ray_start[3], ray_dir[3]; + float gaus_factor; + BVHTreeRayHit hit; + BVHTreeNearest nearest; + short hit_found = 0; + float realPos[3]; + + /* If it's a proximity hit, store distance rate */ + float distRate = -1.0f; + + /* hit data */ + float hitCoord[3]; /* mid-sample hit coordinate */ + int hitFace = -1; /* mid-sample hit face */ + short hitQuad; /* mid-sample hit quad status */ + + /* Supersampling factor */ + if (surface->pixelSamples > 1) { + gaus_factor = gaussianFactors[ss]; + } + else { + gaus_factor = 1.0f; + } + + /* Get current sample position in world coordinates */ + interp_v3_v3v3v3(realPos, + canvasVerts[cPoint->v1].v, + canvasVerts[cPoint->v2].v, + canvasVerts[cPoint->v3].v, cPoint->barycentricWeights[ss].v); + VECCOPY(ray_start, realPos); + VECCOPY(ray_dir, cPoint->invNorm); + + hit.index = -1; + hit.dist = 9999; + nearest.index = -1; + nearest.dist = paint->paint_distance * paint->paint_distance; /* find_nearest search uses squared distance */ + + /* Check volume collision */ + if (paint->collision == MOD_DPAINT_COL_VOLUME || paint->collision == MOD_DPAINT_COL_VOLDIST) + if(BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, &hit, mesh_faces_spherecast_dp, &treeData) != -1) + { + /* We hit a triangle, now check if collision point normal is facing the point */ + + + /* For optimization sake, hit point normal isn't calculated in ray cast loop */ + int v1=mface[hit.index].v1, v2=mface[hit.index].v2, v3=mface[hit.index].v3, quad=(hit.no[0] == 1.0f); + float dot; + + if (quad) {v2=mface[hit.index].v3; v3=mface[hit.index].v4;} + + /* Get hit normal */ + normal_tri_v3( hit.no, mvert[v1].co, mvert[v2].co, mvert[v3].co); + dot = ray_dir[0]*hit.no[0] + ray_dir[1]*hit.no[1] + ray_dir[2]*hit.no[2]; + + /* + * If ray and hit normal are facing same direction + * hit point is inside a closed mesh. + */ + if (dot>=0) + { + /* Add factor on supersample filter */ + ssFactor += gaus_factor; + depth += hit.dist; + hit_found = 1; + + /* + * Mark hit info + */ + if (hitFace == -1) { + VECADDFAC(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ + hitQuad = quad; + hitFace = hit.index; + } + } + } // end of raycast + + /* Check proximity collision */ + if ((paint->collision == MOD_DPAINT_COL_DIST || paint->collision == MOD_DPAINT_COL_VOLDIST) && (!hit_found)) + { + float proxDist = -1.0f; + float hitCo[3]; + short hQuad; + int face; + + /* + * If pure distance proximity, find the nearest point on the mesh + */ + if (!(paint->flags & MOD_DPAINT_PROX_FACEALIGNED)) { + if (BLI_bvhtree_find_nearest(treeData.tree, ray_start, &nearest, mesh_faces_nearest_point_dp, &treeData) != -1) { + proxDist = sqrt(nearest.dist); /* find_nearest returns a squared distance, so gotta change it back to real distance */ + copy_v3_v3(hitCo, nearest.co); + hQuad = (nearest.no[0] == 1.0f); + face = nearest.index; + } + } + else { /* else cast a ray in surface normal direction */ + negate_v3(ray_dir); + hit.index = -1; + hit.dist = paint->paint_distance; + + /* Do a face normal directional raycast, and use that distance */ + if(BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, &hit, mesh_faces_spherecast_dp, &treeData) != -1) + { + proxDist = hit.dist; + VECADDFAC(hitCo, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ + hQuad = (hit.no[0] == 1.0f); + face = hit.index; + } + } + + /* If a hit was found, calculate required values */ + if (proxDist >= 0.0f) { + float dist_rate = proxDist / paint->paint_distance; + + /* Smooth range or color ramp */ + if (paint->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH || + paint->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) { + + /* Limit distance to 0.0 - 1.0 */ + if (dist_rate > 1.0f) dist_rate = 1.0f; + if (dist_rate < 0.0f) dist_rate = 0.0f; + + /* if using smooth falloff, multiply gaussian factor */ + if (paint->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH) { + ssFactor += (1.0f - dist_rate) * gaus_factor; + } + else ssFactor += gaus_factor; + + if (hitFace == -1) { + distRate = dist_rate; + } + } + else ssFactor += gaus_factor; + + hit_found = 1; + + if (hitFace == -1) { + copy_v3_v3(hitCoord, hitCo); + hitQuad = hQuad; + hitFace = face; + } + } // proxDist + } // end proximity check + + /* + * Process color and alpha + */ + if (hit_found) + { + float sampleColor[3]; + float sampleAlpha = 1.0f; + float bandres[4]; + + sampleColor[0] = paint->r; + sampleColor[1] = paint->g; + sampleColor[2] = paint->b; + + /* Get material+textures color on hit point if required */ + if (paint->flags & MOD_DPAINT_USE_MATERIAL) DynamicPaint_GetMaterialColor(sampleColor, &sampleAlpha, paintOb, realPos, hitCoord, hitFace, hitQuad, paint->dm, paint->mat); + + /* Sample colorband if required */ + if ((distRate >= 0.0f) && (paint->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) && do_colorband(paint->paint_ramp, distRate, bandres)) { + if (!(paint->flags & MOD_DPAINT_RAMP_ALPHA)) { + sampleColor[0] = bandres[0]; + sampleColor[1] = bandres[1]; + sampleColor[2] = bandres[2]; + } + sampleAlpha *= bandres[3]; + } + + /* Add AA sample */ + paintColor[0] += sampleColor[0]; + paintColor[1] += sampleColor[1]; + paintColor[2] += sampleColor[2]; + + paintAlpha += sampleAlpha; + numOfHits++; + } + } // end supersampling + + + /* if any sample was inside paint range */ + if (ssFactor > 0.01f) { + + /* apply supersampling results */ + if (surface->pixelSamples > 1) { + ssFactor /= gaussianTotal; + } + + cPoint->state = 2; + + if (paint->flags & MOD_DPAINT_DO_PAINT) { + + float paintWetness = paint->wetness * ssFactor; + + /* Get final pixel color and alpha */ + paintColor[0] /= numOfHits; + paintColor[1] /= numOfHits; + paintColor[2] /= numOfHits; + paintAlpha /= numOfHits; + + /* Multiply alpha value by the ui multiplier */ + paintAlpha = paintAlpha * ssFactor * paint->alpha; + if (paintAlpha > 1.0f) paintAlpha = 1.0f; + + /* + * Mix paint to the surface + */ + DynamicPaint_MixPaintColors(cPoint, paint->flags, paintColor, &paintAlpha, &paintWetness, ×cale); + } + + if (paint->flags & MOD_DPAINT_DO_DISPLACE) { + + if (paint->flags & MOD_DPAINT_ERASE) { + cPoint->depth *= (1.0f - ssFactor); + if (cPoint->depth < 0.0f) cPoint->depth = 0.0f; + } + else { + float normal_scale, tempNorm[3]; + /* + * Calculate normal directional scale of canvas object. + * (Displace maps work in object space) + */ + MVert *canMvert = NULL; + canMvert = canvas->dm->getVertArray(canvas->dm); + normal_tri_v3( tempNorm, canMvert[cPoint->v1].co, canMvert[cPoint->v2].co, canMvert[cPoint->v3].co); + mul_v3_v3 (tempNorm, canvasOb->size); + normal_scale = len_v3(tempNorm); + if (normal_scale<0.01) normal_scale = 0.01; + + depth /= canvas->disp_depth * surface->pixelSamples * normal_scale; + /* do displace */ + if (cPoint->depth < depth) cPoint->depth = depth; + } + } + } + } // end i>0 + } // yy + } // end of pixel loop + } // end if tree exists + + /* free bhv tree */ + free_bvhtree_from_mesh(&treeData); + dm->release(dm); + + } + + return 1; +} + + + +/* +* Paint a particle system to canvas +*/ +static int DynamicPaint_PaintParticles(DynamicPaintCanvasSettings *canvas, ParticleSystem *psys, DynamicPaintPainterSettings *paint, Object *canvasOb, float timescale) +{ + int yy; + ParticleSettings *part=psys->part; + ParticleData *pa = NULL; + PaintSurface *surface = canvas->surface; + + int tWidth = surface->w; + int tHeight = surface->h; + KDTree *tree; + int particlesAdded = 0; + int invalidParticles = 0; + int p = 0; + + if (psys->totpart < 1) return 1; + + /* + * Build a kd-tree to optimize distance search + */ + tree= BLI_kdtree_new(psys->totpart); + + /* loop through particles and insert valid ones to the tree */ + for(p=0, pa=psys->particles; ptotpart; p++, pa++) { + + /* Proceed only if particle is active */ + if(pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN)==0) continue; + else if(pa->alive == PARS_DEAD && (part->flag & PART_DIED)==0) continue; + else if(pa->flag & PARS_NO_DISP || pa->flag & PARS_UNEXIST) continue; + + /* for debug purposes check if any NAN particle proceeds + * For some reason they get past activity check, this should rule most of them out */ + if (isnan(pa->state.co[0]) || isnan(pa->state.co[1]) || isnan(pa->state.co[2])) {invalidParticles++;continue;} + + BLI_kdtree_insert(tree, p, pa->state.co, NULL); + particlesAdded++; + } + + + if (invalidParticles) { + printf("Warning: Invalid particle(s) found!\n"); + } + + /* If no suitable particles were found, exit */ + if (particlesAdded < 1) { + BLI_kdtree_free(tree); + return 1; + } + + /* balance tree */ + BLI_kdtree_balance(tree); + + + /* + * Loop through every pixel + */ + #pragma omp parallel for schedule(static) + for (yy = 0; yy < tHeight; yy++) + { + int xx; + for (xx = 0; xx < tWidth; xx++) { + + int index = xx+tWidth*yy; + PaintSurfacePoint *cPoint = (&surface->point[index]); + int i = cPoint->index; + + /* If this canvas point exists */ + if (i >= 0) + { + int index; + float disp_intersect = 0; + float radius; + float solidradius = paint->particle_radius; + float smooth = paint->particle_smooth; + float strength = 0.0f; + + /* If using per particle radius */ + if (paint->flags & MOD_DPAINT_PART_RAD) { + /* + * If we use per particle radius, we have to sample all particles + * within max radius range + */ + KDTreeNearest *nearest = NULL; + int n, particles = 0; + float range = psys->part->size + smooth; + + particles = BLI_kdtree_range_search(tree, range, cPoint->realCoord, NULL, &nearest); + for(n=0; nparticles + nearest[n].index; + float rad = pa->size + smooth; + float str,smooth_range; + + if (nearest[n].dist > rad) continue; /* if outside range, continue to next one */ + + if ((rad-nearest[n].dist) > disp_intersect) { + disp_intersect = rad-nearest[n].dist; + radius = rad; + } + + /* Continue with paint check */ + if (nearest[n].dist < pa->size) { + /* + * If particle is inside the solid range, no need to continue further + * since no other particle can have higher influence + */ + strength = 1.0f; + break; + } + + smooth_range = (nearest[n].dist - pa->size); + + /* do smoothness if enabled */ + if (smooth) smooth_range/=smooth; + str = 1.0f - smooth_range; + + /* if influence is greater, use this one */ + if (str > strength) strength = str; + + } // end particle loop + + if (nearest) MEM_freeN(nearest); + + } + else { + /* + * With predefined radius, there is no variation between particles. + * It's enough to just find the nearest one. + */ + KDTreeNearest nearest; + float smooth_range; + radius = solidradius + smooth; + + /* Find nearest particle and get distance to it */ + index = BLI_kdtree_find_nearest(tree, cPoint->realCoord, NULL, &nearest); + if (nearest.dist > radius) continue; + + /* distances inside solid radius have maximum influence -> dist = 0 */ + smooth_range = (nearest.dist - solidradius); + if (smooth_range<0) smooth_range=0.0f; + + /* do smoothness if enabled */ + if (smooth) smooth_range/=smooth; + + strength = 1.0f - smooth_range; + disp_intersect = radius - nearest.dist; + } + + if (strength <= 0.0f) continue; + + cPoint->state = 2; + + if (paint->flags & MOD_DPAINT_DO_PAINT) { + + float paintAlpha = paint->alpha * strength; + float paintWetness = paint->wetness * strength; + float paintColor[3]; + + paintColor[0] = paint->r; + paintColor[1] = paint->g; + paintColor[2] = paint->b; + + if (paintAlpha > 1.0f) paintAlpha = 1.0f; + + DynamicPaint_MixPaintColors(cPoint, paint->flags, paintColor, &paintAlpha, &paintWetness, ×cale); + + } + + if (paint->flags & MOD_DPAINT_DO_WETNESS) { + if (cPoint->wetness < (paint->wetness*strength)) cPoint->wetness = paint->wetness*strength; + } + + if (paint->flags & MOD_DPAINT_DO_DISPLACE) { + float sdepth, disp, normal_scale, tempNorm[3]; + MVert *canMvert = NULL; + canMvert = canvas->dm->getVertArray(canvas->dm); + + /* + * Calculate normal directional scale of canvas object. + * (Displace maps work in object space) + */ + normal_tri_v3( tempNorm, canMvert[cPoint->v1].co, canMvert[cPoint->v2].co, canMvert[cPoint->v3].co); + mul_v3_v3 (tempNorm, canvasOb->size); + normal_scale = len_v3(tempNorm); + if (normal_scale<0.01) normal_scale = 0.01; + + /* change falloff type to inverse square to match real displace depth */ + disp_intersect = (1.0f - sqrt(disp_intersect / radius)) * radius; + + /* get displace depth */ + sdepth = (radius - disp_intersect) / normal_scale; + + if (sdepth<0.0f) sdepth = 0.0f; + disp = sdepth / canvas->disp_depth; + if (cPoint->depth < disp) cPoint->depth = disp; + } + } + } + } + + BLI_kdtree_free(tree); + + return 1; +} + + +/* +* Prepare data required by effects for current frame. +* Returns number of steps required +*/ +static int DynamicPaint_Prepare_EffectStep(DynamicPaintCanvasSettings *canvas, float timescale) +{ + double average_dist = 0.0f; + float minimum_dist = 9999.0f; + unsigned int count = 0; + int steps = 1; + PaintSurface *surface = canvas->surface; + int yy, w=surface->w, h=surface->h; + + float fastest_effect, speed_factor; + + /* + * Calculate current frame neighbouring pixel distances + * and average distance between those neighbours + */ + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int i; + PaintSurfacePoint *cPoint = (&surface->point[xx+w*yy]); + + if (cPoint->index != -1) + for (i=0; i<8; i++) { + int x,y, index; + PaintSurfacePoint *tPoint; + + x = xx + neighX[i]; + y = yy + neighY[i]; + + index = x+w*y; + if (cPoint->neighbours[i] != -1) { + + tPoint = (&surface->point[index]); + cPoint->neighbour_dist[i] = len_v3v3(cPoint->realCoord, tPoint->realCoord); + if (cPoint->neighbour_dist[i] < minimum_dist) minimum_dist = cPoint->neighbour_dist[i]; + + average_dist += cPoint->neighbour_dist[i]; + count++; + } + } + } + + } // end pixel loop + + /* + * Note: some other method could be better + * Right now double precision may cause noticable error on huge + * texture sizes (8096*8096*8 = 500 000 000 added values) + */ + average_dist /= (double)count; + + /* Limit minimum distance (used to define substeps) + * to 1/2 of the average + */ + if (minimum_dist < (average_dist/2.0f)) minimum_dist=average_dist/2.0f; + + /* Get fastest effect speed and scale other effects according to it */ + fastest_effect = canvas->spread_speed; + if (canvas->drip_speed > fastest_effect) fastest_effect = canvas->drip_speed; + if (canvas->shrink_speed > fastest_effect) fastest_effect = canvas->shrink_speed; + + /* Number of steps depends on surface res, effect speed and timescale */ + speed_factor = (float)surface->w/256.0f * timescale; + steps = (int)ceil(speed_factor*fastest_effect); + + speed_factor /= (float)steps; + + /* + * Now calculate final per pixel speed ratio to neighbour_dist[] array + */ + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + float dd; + for (xx = 0; xx < w; xx++) + { + int i; + PaintSurfacePoint *cPoint = (&surface->point[xx+w*yy]); + + if (cPoint->index != -1) + for (i=0; i<8; i++) { + if (cPoint->neighbours[i] != -1) { + dd = (cPoint->neighbour_dist[i]neighbour_dist[i]; + cPoint->neighbour_dist[i] = minimum_dist / dd * speed_factor; + } + } + } + } // end pixel loop + + //printf("Average distance is %f, minimum distance is %f\n", average_dist, minimum_dist); + + return steps; +} + + +/* +* Clean effect data +*/ +static void DynamicPaint_Clean_EffectStep(DynamicPaintCanvasSettings *canvas, float timescale) +{ + //PaintSurface *surface = canvas->surface; +} + + +/* +* Processes active effect step. +*/ +static void DynamicPaint_Do_EffectStep(DynamicPaintCanvasSettings *canvas, PaintSurfacePoint *prevPoint, float timescale) +{ + PaintSurface *surface = canvas->surface; + int yy, w=surface->w, h=surface->h; + + /* + * Spread Effect + */ + if (canvas->effect & MOD_DPAINT_EFFECT_DO_SPREAD) { + + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int index = xx+w*yy; + int i, validPoints = 0; + float totalAlpha = 0.0f; + PaintSurfacePoint *cPoint = (&surface->point[index]); /* Current source point */ + PaintSurfacePoint *ePoint; /* Effect point to shift values into */ + + /* + * Only reads values from the surface copy (prevPoint[]), + * so this one is thread safe + */ + + /* Loop through neighbouring pixels */ + for (i=0; i<8; i++) { + int nIndex; + float factor, alphaAdd = 0.0f; + + nIndex = surface->point[index].neighbours[i]; + if (nIndex == -1) continue; + + /* + * Find neighbour cells that have higher wetness + * and expand it to this cell as well. + */ + ePoint = (&prevPoint[nIndex]); + validPoints++; + totalAlpha += ePoint->e_alpha; + + if (ePoint->wetness <= cPoint->wetness) continue; + factor = ePoint->wetness/8 * (ePoint->wetness - cPoint->wetness) * surface->point[index].neighbour_dist[i] * canvas->spread_speed; + + if (ePoint->e_alpha > cPoint->e_alpha) { + alphaAdd = ePoint->e_alpha/8 * (ePoint->wetness*ePoint->e_alpha - cPoint->wetness*cPoint->e_alpha) * surface->point[index].neighbour_dist[i] * canvas->spread_speed; + } + + + /* If this pixel has existing paint, we have to blend it properly */ + if (cPoint->e_alpha) { + float invFactor = 1.0f - factor; + cPoint->e_color[0] = cPoint->e_color[0]*invFactor + ePoint->e_color[0]*factor; + cPoint->e_color[1] = cPoint->e_color[1]*invFactor + ePoint->e_color[1]*factor; + cPoint->e_color[2] = cPoint->e_color[2]*invFactor + ePoint->e_color[2]*factor; + + cPoint->e_alpha += alphaAdd; + cPoint->wetness += factor; + } + else { + /* If there is no existing paint, just replace the color */ + cPoint->e_color[0] = ePoint->e_color[0]; + cPoint->e_color[1] = ePoint->e_color[1]; + cPoint->e_color[2] = ePoint->e_color[2]; + + cPoint->e_alpha += alphaAdd; + cPoint->wetness += factor; + } + + if (cPoint->e_alpha > 1.0f) cPoint->e_alpha = 1.0f; + } + + /* For antialiasing sake, don't let alpha go much higher than average alpha of neighbours */ + if (validPoints && (cPoint->e_alpha > (totalAlpha/validPoints+0.25f))) { + cPoint->e_alpha = (totalAlpha/validPoints+0.25f); + if (cPoint->e_alpha>1.0f) cPoint->e_alpha = 1.0f; + } + } + } // end pixel loop + } // end spread effect + + + /* + * Drip Effect + */ + if (canvas->effect & MOD_DPAINT_EFFECT_DO_DRIP) + { + memcpy(prevPoint, surface->point, surface->w*surface->h*sizeof(struct PaintSurfacePoint)); + + //#pragma omp parallel for schedule(static) + for (yy = 1; yy < h-1; yy++) + { + int xx; + for (xx = 1; xx < w-1; xx++) + { + int index = xx+w*yy; + float factor = 0.0f, drip_strength = 0.0f; + PaintSurfacePoint *cPoint = (&prevPoint[index]); /* Current source point to read */ + PaintSurfacePoint *dPoint = (&surface->point[index]); /* Current source point to write */ + PaintSurfacePoint *ePoint; /* Effect point to shift values into */ + PaintSurfacePoint *ePoint2; /* Effect point to shift values into */ + int nIndex; + + /* + * Because the neighbour pixels go in 45 degree (pi/4) slices + * get drip factor in that range + */ + float dirMod = fmod(cPoint->gravity_dir,0.785398163); + float stFac=dirMod/0.785398163; + float facTotal; + int neighPixel,neighPixel2; + + /* Skip if wetness is too low */ + drip_strength = cPoint->wetness - 0.1f; + if (drip_strength < 0) continue; + + + /* Get first neighpixel index for neighbours[] array */ + neighPixel = (int)floor(cPoint->gravity_dir/6.2831853f * 8.0f); + + if (neighPixel > 7) neighPixel = 7; /* Shouldn't happen but just in case */ + if (neighPixel < 0) neighPixel = 0; + + /* Make sure the neighbour exists */ + nIndex = surface->point[index].neighbours[neighPixel]; + if (nIndex == -1) continue; + ePoint = (&surface->point[nIndex]); + + + /* + * Second neighbouring point + */ + neighPixel2 = neighPixel + 1; + if (neighPixel2 > 7) neighPixel2 = 0; + + /* Make sure the neighbour exists */ + nIndex = surface->point[index].neighbours[neighPixel2]; + if (nIndex == -1) continue; + ePoint2 = (&surface->point[nIndex]); + + + /* Do some adjustments to dripping speed */ + factor = 0.3 * canvas->drip_speed; + if (drip_strength < 2.5f) factor *= drip_strength; + + /* Add values to the pixel below -> drip */ + ePoint->e_color[0] = cPoint->e_color[0]; + ePoint->e_color[1] = cPoint->e_color[1]; + ePoint->e_color[2] = cPoint->e_color[2]; + + ePoint->e_alpha += cPoint->e_alpha * factor * surface->point[index].neighbour_dist[neighPixel] * (1.0f - stFac); + ePoint->wetness += cPoint->wetness * factor * surface->point[index].neighbour_dist[neighPixel] * (1.0f - stFac); + + if (stFac > 0.0f) { + ePoint2->e_color[0] = cPoint->e_color[0]; + ePoint2->e_color[1] = cPoint->e_color[1]; + ePoint2->e_color[2] = cPoint->e_color[2]; + } + + ePoint2->e_alpha += cPoint->e_alpha * factor * surface->point[index].neighbour_dist[neighPixel2] * stFac; + ePoint2->wetness += cPoint->wetness * factor * surface->point[index].neighbour_dist[neighPixel2] * stFac; + + //dPoint->e_alpha -= cPoint->e_alpha * factor; + facTotal = surface->point[index].neighbour_dist[neighPixel] * (1.0f - stFac) + surface->point[index].neighbour_dist[neighPixel2] * stFac; + dPoint->wetness -= cPoint->wetness * factor * facTotal; + + } + } // end pixel loop + + /* Keep wetness values within acceptable range */ + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int index = xx+w*yy; + + PaintSurfacePoint *cPoint = (&surface->point[index]); + + if (cPoint->e_alpha > 1.0f) cPoint->e_alpha=1.0f; + if (cPoint->wetness > 3.5f) cPoint->wetness=3.5f; + + if (cPoint->e_alpha < 0.0f) cPoint->e_alpha=0.0f; + if (cPoint->wetness < 0.0f) cPoint->wetness=0.0f; + + } + } // end pixel loop + } // end dripping effect + + + + /* + * Shrink Effect + */ + if (canvas->effect & MOD_DPAINT_EFFECT_DO_SHRINK) { + + #pragma omp parallel for schedule(static) + for (yy = 0; yy < h; yy++) + { + int xx; + for (xx = 0; xx < w; xx++) + { + int index = xx+w*yy; + int i, validPoints = 0; + float totalAlpha = 0.0f; + PaintSurfacePoint *cPoint = (&surface->point[index]); /* Current source point */ + PaintSurfacePoint *ePoint; /* Effect point to shift values into */ + + /* Loop through neighbouring pixels */ + for (i=0; i<8; i++) { + int nIndex; + float factor, e_factor, w_factor; + + nIndex = surface->point[index].neighbours[i]; + + if (nIndex == -1) continue; + + /* + * Find neighbouring cells that have lower alpha + * and decrease this point alpha towards that level. + */ + ePoint = (&prevPoint[nIndex]); + validPoints++; + totalAlpha += ePoint->e_alpha; + + if (cPoint->alpha <= 0.0f && cPoint->e_alpha <= 0.0f && cPoint->wetness <= 0.0f) continue; + factor = (1.0f - ePoint->alpha)/8 * (cPoint->alpha - ePoint->alpha) * surface->point[index].neighbour_dist[i] * canvas->shrink_speed; + if (factor < 0.0f) factor = 0.0f; + + e_factor = (1.0f - ePoint->e_alpha)/8 * (cPoint->e_alpha - ePoint->e_alpha) * surface->point[index].neighbour_dist[i] * canvas->shrink_speed; + if (e_factor < 0.0f) e_factor = 0.0f; + + w_factor = (1.0f - ePoint->wetness)/8 * (cPoint->wetness - ePoint->wetness) * surface->point[index].neighbour_dist[i] * canvas->shrink_speed; + if (w_factor < 0.0f) w_factor = 0.0f; + + + if (factor) { + cPoint->alpha -= factor; + if (cPoint->alpha < 0.0f) cPoint->alpha = 0.0f; + cPoint->wetness -= factor; + + } + else { + cPoint->e_alpha -= e_factor; + if (cPoint->e_alpha < 0.0f) cPoint->e_alpha = 0.0f; + cPoint->wetness -= w_factor; + if (cPoint->wetness < 0.0f) cPoint->wetness = 0.0f; + } + } + } + } // end pixel loop + } // end spread effect +} + + + +/* +* Do Dynamic Paint Step. Paints scene paint objects of current frame to canvas. +*/ +static int DynamicPaint_DoStep(Scene *scene, Object *ob, DynamicPaintModifierData *pmd, float timescale) +{ + DynamicPaintCanvasSettings *canvas = pmd->canvas; + PaintSurface *surface = canvas->surface; + Base *base = NULL; + MVert *mvert = NULL; + MFace *mface = NULL; + MTFace *tface = NULL; + DerivedMesh *dm = canvas->dm; + + int w, h; + int yy; + + int canvasNumOfVerts; + Vec3f *canvasVerts = NULL; + int canvasNumOfFaces; + FaceAdv *canvasInvNormals = NULL; + + if (!dm) { + dpError(canvas, "Invalid canvas mesh."); + return 0; + } + + mvert = dm->getVertArray(dm); + mface = dm->getFaceArray(dm); + tface = DM_get_face_data_layer(dm, CD_MTFACE); + canvasNumOfFaces = dm->getNumFaces(dm); + + w = canvas->surface->w; + h = canvas->surface->h; + + /* + * Make a transformed copy of canvas derived mesh vertices to avoid recalculation. + */ + canvasNumOfVerts = dm->getNumVerts(dm); + canvasVerts = (struct Vec3f *) MEM_mallocN(canvasNumOfVerts*sizeof(struct Vec3f), "Dynamic Paint Transformed canvas"); + if (canvasVerts == NULL) {dpError(canvas, "Not enough free memory."); return 0;} + + #pragma omp parallel for schedule(static) + for (yy=0; yyobmat, canvasVerts[yy].v); + } + + + /* + * Calculate temp per face normals using transformed coordinates. + * (To not have to calculate same normal for millions of pixels) + */ + canvasInvNormals = (struct FaceAdv *) MEM_callocN(canvasNumOfFaces*sizeof(struct FaceAdv), "Dynamic Paint canvas normals"); + if (canvasInvNormals == NULL) {dpError(canvas, "Not enough free memory."); return 0;} + + + #pragma omp parallel for schedule(static) + for (yy=0; yysurface->point[index].index; + quad = canvas->surface->point[index].quad; + + /* only continue if current pixel has 3D coordinates */ + if (i >= 0) + { + PaintSurfacePoint *cPoint = (&canvas->surface->point[index]); + + /* + * Do dissolve / drying / flatten + */ + if (cPoint->wetness > 0.0f) { + + /* Every drying step Blends wet paint to the background. */ + float invAlpha = 1.0f - cPoint->e_alpha; + cPoint->color[0] = cPoint->color[0]*invAlpha + cPoint->e_color[0]*cPoint->e_alpha; + cPoint->color[1] = cPoint->color[1]*invAlpha + cPoint->e_color[1]*cPoint->e_alpha; + cPoint->color[2] = cPoint->color[2]*invAlpha + cPoint->e_color[2]*cPoint->e_alpha; + + cPoint->state = 1; + + /* only increase alpha if wet paint has higher */ + if (cPoint->e_alpha > cPoint->alpha) cPoint->alpha = cPoint->e_alpha; + + /* Now dry it ;o */ + if (canvas->flags & MOD_DPAINT_DRY_LOG) cPoint->wetness *= 1.0f - (1.0 / (canvas->dry_speed/timescale)); + else cPoint->wetness -= 1.0f/canvas->dry_speed*timescale; + } + + /* If effect layer is completely dry, make sure it's marked empty */ + if (cPoint->wetness <= 0.0f) { + cPoint->wetness = 0.0f; + cPoint->e_alpha = 0.0f; + cPoint->state = 0; + } + + if (canvas->flags & (MOD_DPAINT_DISSOLVE)) { + + cPoint->alpha -= 1.0f/canvas->diss_speed*timescale; + if (cPoint->alpha < 0.0f) cPoint->alpha = 0.0f; + + cPoint->e_alpha -= 1.0f/canvas->diss_speed*timescale; + if (cPoint->e_alpha < 0.0f) cPoint->e_alpha = 0.0f; + } + + if (canvas->flags & (MOD_DPAINT_FLATTEN)) { + + cPoint->depth -= 1.0f/canvas->dflat_speed*timescale; + if (cPoint->depth < 0.0f) cPoint->depth = 0.0f; + } + + + /* + * Calculate current 3D-position of each texture pixel + */ + interp_v3_v3v3v3( cPoint->realCoord, + canvasVerts[cPoint->v1].v, + canvasVerts[cPoint->v2].v, + canvasVerts[cPoint->v3].v, cPoint->barycentricWeights[0].v); + + /* Calculate current pixel surface normal */ + if(mface[cPoint->index].flag & ME_SMOOTH) { + normal_short_to_float_v3(n1, mvert[cPoint->v1].no); + normal_short_to_float_v3(n2, mvert[cPoint->v2].no); + normal_short_to_float_v3(n3, mvert[cPoint->v3].no); + + interp_v3_v3v3v3( cPoint->invNorm, + n1, n2, n3, cPoint->barycentricWeights[0].v); + normalize_v3(cPoint->invNorm); + negate_v3(cPoint->invNorm); + } + else { + if (cPoint->quad) {VECCOPY(cPoint->invNorm, canvasInvNormals[cPoint->index].no_q);} + else {VECCOPY(cPoint->invNorm, canvasInvNormals[cPoint->index].no);} + } + + + /* + * Get current gravity direction of pixel in UV space. + * World gravity direction is negative z-axis + * - if any active effect requires it + */ + if (canvas->effect & MOD_DPAINT_EFFECT_DO_DRIP) { + + cPoint->gravity_dir = 0.0f; + cPoint->gravity_rate = 1.0f - abs(cPoint->invNorm[2]); + + if (cPoint->gravity_rate > 0.01) + { + float uv1[3], uv2[3], uv3[3], unormal[3]; + int v2i, v3i; + + v2i = (cPoint->quad) ? 2 : 1; + v3i = (cPoint->quad) ? 3 : 2; + + /* + * Apply z-coodrinate (gravity dir) to uv-vertices + */ + uv1[0] = tface[cPoint->index].uv[0][0]; + uv1[1] = tface[cPoint->index].uv[0][1]; + uv1[2] = canvasVerts[cPoint->v1].v[2]; + + uv2[0] = tface[cPoint->index].uv[v2i][0]; + uv2[1] = tface[cPoint->index].uv[v2i][1]; + uv2[2] = canvasVerts[cPoint->v2].v[2]; + + uv3[0] = tface[cPoint->index].uv[v3i][0]; + uv3[1] = tface[cPoint->index].uv[v3i][1]; + uv3[2] = canvasVerts[cPoint->v3].v[2]; + + /* Calculate a new normal vector for that generated face */ + normal_tri_v3( unormal, uv1, uv2, uv3); + + /* Normalize u and v part of it */ + normalize_v2(unormal); + + /* + * use direction of that 2d vector as gravity direction in uv space + * (Convert from -pi->pi to 0->2pi to use with neighbour table) + */ + cPoint->gravity_dir = fmod((atan2(unormal[1], unormal[0])+6.28318531f), 6.28318531f); + } + } // end if (effect) + } // end i>0 + } // yy + } // end of loop xx + MEM_freeN(canvasInvNormals); + + + /* + * Loop through every object in scene and + * do painting for active paint objects + */ + { + Object *otherobj = NULL; + ModifierData *md = NULL; + + base = scene->base.first; + + while(base) + { + otherobj = base->object; + + if(!otherobj) + { + base= base->next; + continue; + } + base= base->next; + + md = modifiers_findByType(otherobj, eModifierType_DynamicPaint); + + /* check if target has an active dp modifier */ + if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) + { + DynamicPaintModifierData *pmd2 = (DynamicPaintModifierData *)md; + + /* Make sure we're dealing with a painter */ + if((pmd2->type & MOD_DYNAMICPAINT_TYPE_PAINT) && pmd2->paint) + { + DynamicPaintPainterSettings *paint = pmd2->paint; + + /* + * Make sure at least one influence is enabled + */ + if (!(paint->flags & MOD_DPAINT_DO_PAINT || paint->flags & MOD_DPAINT_DO_DISPLACE)) continue; + + + /* Check if painter has a particle system selected + * -> if so, do particle painting */ + if (paint->collision == MOD_DPAINT_COL_PSYS) + { + if (paint && paint->psys && paint->psys->part && paint->psys->part->type==PART_EMITTER) + if (psys_check_enabled(otherobj, paint->psys)) { + /* + * Paint a particle system + */ + DynamicPaint_PaintParticles(canvas, paint->psys, paint, ob, timescale); + } + } + else { + /* + * Paint a object mesh + */ + DynamicPaint_PaintMesh(canvas, canvasVerts, paint, ob, otherobj, timescale); + } + } /* end of collision check (Is valid paint modifier) */ + } + } + } + + /* Free per frame canvas data */ + MEM_freeN(canvasVerts); + + + /* + * DO EFFECTS + */ + if (canvas->effect) + { + unsigned int steps = 1, s; + + /* Allocate memory for surface previous points to read unchanged values from */ + PaintSurfacePoint *prevPoint = (struct PaintSurfacePoint *) MEM_mallocN(surface->w*surface->h*sizeof(struct PaintSurfacePoint), "PaintSurfaceDataTemp"); + if (prevPoint == NULL) {dpError(canvas, "Not enough free memory."); return 0;} + + /* Prepare effects and get number of required effect-substeps */ + steps = DynamicPaint_Prepare_EffectStep(canvas, timescale); + + /* + * Do Effects steps + */ + for (s = 0; s < steps; s++) + { + /* Copy current surface to the previous surface array */ + memcpy(prevPoint, surface->point, surface->w*surface->h*sizeof(struct PaintSurfacePoint)); + + DynamicPaint_Do_EffectStep(canvas, prevPoint, timescale); + + } + + /* Free temporary effect data */ + MEM_freeN(prevPoint); + DynamicPaint_Clean_EffectStep(canvas, timescale); + + } // end effects + + return 1; +} + + +/* +* Outputs an image file from canvas data. +*/ +void dynamic_paint_output_image(struct DynamicPaintCanvasSettings *canvas, char* filename, short type, short source) +{ + int yy; + ImBuf* mhImgB = NULL; + + int tWidth, tHeight; + PaintSurface *surface = canvas->surface; + + if (!surface) {printf("Canvas save failed: Invalid surface.\n");return;} + + /* Validate output file path */ + BLI_path_abs(filename, G.main->name); + BLI_make_existing_file(filename); + + /* Print save message */ + if (source == DPOUTPUT_WET) { + printf("Saving wetmap : %s\n", filename); + } else if (source == DPOUTPUT_DISPLACE) { + printf("Saving displacement map : %s\n", filename); + } else { + printf("Saving color map : %s\n", filename); + } + + tWidth = surface->w; + tHeight = surface->h; + + /* Init image buffer */ + mhImgB = IMB_allocImBuf(tWidth, tHeight, 32, IB_rectfloat); + if (mhImgB == NULL) {printf("Image save failed: Not enough free memory.\n");return;} + + #pragma omp parallel for schedule(static) + for (yy = 0; yy < tHeight; yy++) + { + int xx; + for (xx = 0; xx < tWidth; xx++) + { + int pos=(xx+tWidth*yy)*4; /* image buffer position */ + int index=(xx+tWidth*yy); /* surface point */ + PaintSurfacePoint *cPoint = (&surface->point[index]); + + + /* Set values of preferred type */ + if (source == DPOUTPUT_WET) { + float value = (cPoint->wetness > 1.0f) ? 1.0f : cPoint->wetness; + mhImgB->rect_float[pos]=value; + mhImgB->rect_float[pos+1]=value; + mhImgB->rect_float[pos+2]=value; + mhImgB->rect_float[pos+3]=1.0f; + } + else if (source == DPOUTPUT_DISPLACE) { + + float depth = cPoint->depth; + + if (canvas->disp_type == MOD_DPAINT_DISP_DISPLACE) { + depth = (0.5f - depth); + if (depth < 0.0f) depth = 0.0f; + if (depth > 1.0f) depth = 1.0f; + } + + mhImgB->rect_float[pos]=depth; + mhImgB->rect_float[pos+1]=depth; + mhImgB->rect_float[pos+2]=depth; + mhImgB->rect_float[pos+3]=1.0f; + } + else { /* DPOUTPUT_PAINT */ + + float invAlpha = 1.0f - cPoint->e_alpha; + + /* If base layer already has a color, blend it */ + if (cPoint->alpha) { + mhImgB->rect_float[pos] = cPoint->color[0] * invAlpha + cPoint->e_color[0] * cPoint->e_alpha; + mhImgB->rect_float[pos+1] = cPoint->color[1] * invAlpha + cPoint->e_color[1] * cPoint->e_alpha; + mhImgB->rect_float[pos+2] = cPoint->color[2] * invAlpha + cPoint->e_color[2] * cPoint->e_alpha; + } + else { + /* Else use effect layer color */ + mhImgB->rect_float[pos] = cPoint->e_color[0]; + mhImgB->rect_float[pos+1] = cPoint->e_color[1]; + mhImgB->rect_float[pos+2] = cPoint->e_color[2]; + } + + /* Set use highest alpha */ + mhImgB->rect_float[pos+3] = (cPoint->e_alpha > cPoint->alpha) ? cPoint->e_alpha : cPoint->alpha; + + /* Multiply color by alpha if enabled */ + if (canvas->flags & MOD_DPAINT_MULALPHA) { + mhImgB->rect_float[pos] *= mhImgB->rect_float[pos+3]; + mhImgB->rect_float[pos+1] *= mhImgB->rect_float[pos+3]; + mhImgB->rect_float[pos+2] *= mhImgB->rect_float[pos+3]; + } + } + } + } + + /* Save image buffer */ + if (type == DPOUTPUT_JPEG) { /* JPEG */ + mhImgB->ftype= JPG|95; + IMB_rect_from_float(mhImgB); + imb_savejpeg(mhImgB, filename, IB_rectfloat); + } +#ifdef WITH_OPENEXR + else if (type == DPOUTPUT_OPENEXR) { /* OpenEXR 32-bit float */ + mhImgB->ftype = OPENEXR | OPENEXR_COMPRESS; + IMB_rect_from_float(mhImgB); + imb_save_openexr(mhImgB, filename, IB_rectfloat); + } +#endif + else { /* DPOUTPUT_PNG */ + mhImgB->ftype= PNG|95; + IMB_rect_from_float(mhImgB); + imb_savepng(mhImgB, filename, IB_rectfloat); + } + + IMB_freeImBuf(mhImgB); +} + + +/* +* Update required data that didn't get updated +* by ED_update_for_newframe() +*/ +static void update_scene_data(struct Main *bmain, int frame) +{ + + Tex *tex; + /* + * Loop through textures and update voxel data + */ + for (tex= bmain->tex.first; tex; tex= tex->id.next) { + if(tex->id.us && tex->type==TEX_VOXELDATA) { + cache_voxeldata(tex, frame); + } + } +} + + +/* +* Do actual bake operation. +* returns 0 on failture. +*/ +static int DynamicPaint_Bake(bContext *C, struct DynamicPaintModifierData *pmd, Object *cObject) +{ + + DynamicPaintCanvasSettings *canvas; + Scene *scene= CTX_data_scene(C); + wmWindow *win = CTX_wm_window(C); + int frame = 1; + int frames; + + canvas = pmd->canvas; + if (!canvas) {dpError(canvas, "Invalid canvas."); return 0;} + + frames = canvas->end_frame - canvas->start_frame + 1; + if (frames <= 0) {dpError(canvas, "No frames to bake."); return 0;} + + /* + * Set frame to start point (also inits modifier data) + */ + frame = canvas->start_frame; + scene->r.cfra = (int)frame; + ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1); + update_scene_data(CTX_data_main(C), frame); + + /* Init surface */ + if (!dynamicPaint_createCanvasSurface(canvas)) return 0; + + + /* + * Loop through selected frames + */ + for (frame=canvas->start_frame; frame<=canvas->end_frame; frame++) + { + float timescale = 1.0f / (canvas->substeps+1); + int st; + float progress = (frame - canvas->start_frame) / (float)frames * 100; + + /* If user requested stop (esc), quit baking */ + if (blender_test_break()) return 0; + + /* Update progress bar cursor */ + WM_timecursor(win, (int)progress); + + printf("DynamicPaint: Baking frame %i\n", frame); + + /* + * Do calculations for every substep + * Note: these have to be from previous frame + */ + if (frame != canvas->start_frame) { + for (st = 1; st <= canvas->substeps; st++) + { + float subframe = ((float) st) / (canvas->substeps+1); + + /* Update frame if we have proceed */ + scene->r.cfra = (int)frame - 1; + scene->r.subframe = subframe; + ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1); + + if (!DynamicPaint_DoStep(scene, cObject, pmd, timescale)) return 0; + } + + /* + * Change to next whole frame + */ + scene->r.cfra = (int)frame; + scene->r.subframe = 0.0f; + ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1); + update_scene_data(CTX_data_main(C), frame); + + } + + if (!DynamicPaint_DoStep(scene, cObject, pmd, timescale)) return 0; + + /* + * Just in case, check if any output is enabled + * Don't cancel the bake because user may have keyframed outputs + */ + if (!(canvas->output & MOD_DPAINT_OUT_PAINT || canvas->output & MOD_DPAINT_OUT_WET || canvas->output & MOD_DPAINT_OUT_DISP)) { + printf("Skipping output for frame %i.\n", frame); + continue; + } + + /* + * Save output images + */ + { + char filename[250]; + char pad[4]; + char wet[4]; + char disp[4]; + + /* Add frame number padding */ + if (frame<10) sprintf(pad,"000"); + else if (frame<100) sprintf(pad,"00"); + else if (frame<1000) sprintf(pad,"0"); + else pad[0] = '\0'; + + /* Check if paint and wet map filename is same and fix if necessary */ + if (!strcmp(canvas->paint_output_path, canvas->wet_output_path)) sprintf(wet,"wet"); + else wet[0] = '\0'; + /* same for displacement map */ + if (!strcmp(canvas->paint_output_path, canvas->displace_output_path)) sprintf(disp,"disp"); + else if (!strcmp(canvas->wet_output_path, canvas->displace_output_path)) sprintf(disp,"disp"); + else disp[0] = '\0'; + + /* color map */ + if (canvas->output & MOD_DPAINT_OUT_PAINT) { + sprintf(filename, "%s%s%i.png", canvas->paint_output_path, pad, (int)frame); + + dynamic_paint_output_image(canvas, filename, DPOUTPUT_PNG, DPOUTPUT_PAINT); + } + + /* wetmap */ + if (canvas->output & MOD_DPAINT_OUT_WET) { + sprintf(filename, "%s%s%s%i.png", canvas->wet_output_path, wet, pad, (int)frame); + + dynamic_paint_output_image(canvas, filename, DPOUTPUT_PNG, DPOUTPUT_WET); + } + + /* displacement map */ + if (canvas->output & MOD_DPAINT_OUT_DISP) { + /* OpenEXR or PNG */ + int format = (canvas->disp_format & MOD_DPAINT_DISPFOR_OPENEXR) ? DPOUTPUT_OPENEXR : DPOUTPUT_PNG; + char ext[4]; + if (canvas->disp_format & MOD_DPAINT_DISPFOR_OPENEXR) sprintf(ext,"exr"); else sprintf(ext,"png"); + sprintf(filename, "%s%s%s%i.%s", canvas->displace_output_path, disp, pad, (int)frame, ext); + + dynamic_paint_output_image(canvas, filename, format, DPOUTPUT_DISPLACE); + } + } + } + + return 1; +} + + +/* +* Updates baking status for every paint object in the scene +* returns 0 if no paint objects found. +*/ +static int DynamicPaint_PainterSetBaking(Scene *scene, short baking) +{ + Object *otherobj = NULL; + ModifierData *md = NULL; + int count = 0; + + Base *base = scene->base.first; + + while(base) + { + + otherobj = base->object; + + if(!otherobj) + { + base= base->next; + continue; + } + + md = modifiers_findByType(otherobj, eModifierType_DynamicPaint); + + /* check if target has an active dp modifier */ + if(md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) + { + DynamicPaintModifierData *pmd2 = (DynamicPaintModifierData *)md; + /* Make sure we're dealing with a painter */ + if((pmd2->type & MOD_DYNAMICPAINT_TYPE_PAINT) && pmd2->paint) + { + pmd2->baking = baking; + count++; + } + } + base= base->next; + } + + if (count) return 1; + + return 0; +} + + +/* +* An operator call to bake dynamic paint simulation for active object +*/ +static int dynamic_paint_bake_all(bContext *C, wmOperator *op) +{ + + DynamicPaintModifierData *pmd = NULL; + Scene *scene = CTX_data_scene(C); + Object *cObject = CTX_data_active_object(C); + int status = 0; + double timer = PIL_check_seconds_timer(); + + /* + * Get modifier data + */ + pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint); + if (!pmd) {printf("DynamicPaint bake failed: No Dynamic Paint modifier found.\n"); return 0;} + + /* Make sure we're dealing with a canvas */ + if (!pmd->canvas) {printf("DynamicPaint bake failed: Invalid canvas.\n"); return 0;} + + /* + * Set state to baking and init surface + */ + pmd->canvas->error[0] = '\0'; + pmd->baking = 1; + + if (DynamicPaint_PainterSetBaking(scene, 1)) { + + G.afbreek= 0; /* reset blender_test_break*/ + + /* Bake Dynamic Paint */ + status = DynamicPaint_Bake(C, pmd, cObject); + + /* + * Clean bake stuff + */ + pmd->baking = 0; + DynamicPaint_PainterSetBaking(scene, 0); + dynamicPaint_cleanCanvasSurface(pmd->canvas); + + /* Restore cursor back to normal */ + WM_cursor_restore(CTX_wm_window(C)); + + } + else { + dpError(pmd->canvas, "No paint objects."); + status = 0; + } + + /* Bake was successful: + * Report for ended bake and how long it took */ + if (status) { + + /* Format time string */ + char timestr[30]; + double time = PIL_check_seconds_timer() - timer; + int tmp_val; + timestr[0] = '\0'; + + /* days (just in case someone actually has a very slow pc) */ + tmp_val = (int)floor(time / 86400.0f); + if (tmp_val > 0) sprintf(timestr, "%i Day(s) - ", tmp_val); + /* hours */ + time -= 86400.0f * tmp_val; + tmp_val = (int)floor(time / 3600.0f); + if (tmp_val > 0) sprintf(timestr, "%s%i h ", timestr, tmp_val); + /* minutes */ + time -= 3600.0f * tmp_val; + tmp_val = (int)floor(time / 60.0f); + if (tmp_val > 0) sprintf(timestr, "%s%i min ", timestr, tmp_val); + /* seconds */ + time -= 60.0f * tmp_val; + tmp_val = (int)ceil(time); + sprintf(timestr, "%s%i s", timestr, tmp_val); + + /* Show bake info */ + sprintf(pmd->canvas->ui_info, "Bake Complete! (Time: %s)", timestr); + printf("%s\n", pmd->canvas->ui_info); + } + else { + if (strlen(pmd->canvas->error)) { /* If an error occured */ + sprintf(pmd->canvas->ui_info, "Bake Failed: %s", pmd->canvas->error); + BKE_report(op->reports, RPT_ERROR, pmd->canvas->ui_info); + } + else { /* User cancelled the bake */ + sprintf(pmd->canvas->ui_info, "Baking Cancelled!"); + BKE_report(op->reports, RPT_WARNING, pmd->canvas->ui_info); + } + + /* Print failed bake to console */ + printf("Baking Cancelled!\n"); + + } + + return status; +} + + +/***************************** Operators ******************************/ + +static int dynamicpaint_bake_exec(bContext *C, wmOperator *op) +{ + + /* Bake dynamic paint */ + if(!dynamic_paint_bake_all(C, op)) { + return OPERATOR_CANCELLED;} + + return OPERATOR_FINISHED; +} + +void DPAINT_OT_bake(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Dynamic Paint Bake"; + ot->description= "Bake dynamic paint"; + ot->idname= "DPAINT_OT_bake"; + + /* api callbacks */ + ot->exec= dynamicpaint_bake_exec; + ot->poll= ED_operator_object_active_editable; +} + diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index d0fee1d5bd5..251aa7259d7 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -49,6 +49,7 @@ #include "DNA_particle_types.h" #include "DNA_smoke_types.h" #include "DNA_scene_types.h" +#include "DNA_dynamicpaint_types.h" #include "BLI_blenlib.h" #include "BLI_math.h" @@ -3455,6 +3456,14 @@ void object_remove_particle_system(Scene *scene, Object *ob) smd->flow->psys = NULL; } + if((md = modifiers_findByType(ob, eModifierType_DynamicPaint))) + { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + if((pmd->type == MOD_DYNAMICPAINT_TYPE_PAINT) && pmd->paint && pmd->paint->psys) + if(pmd->paint->psys == psys) + pmd->paint->psys = NULL; + } + /* clear modifier */ psmd= psys_get_modifier(ob, psys); BLI_remlink(&ob->modifiers, psmd); diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 634634f02e5..3901ef9aff5 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -60,6 +60,7 @@ float area_poly_v3(int nr, float verts[][3], const float normal[3]); float dist_to_line_v2(const float p[2], const float l1[2], const float l2[2]); float dist_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2]); +void closest_to_line_segment_v2(float *closest, float p[2], float l1[2], float l2[2]); float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]); float closest_to_line_v3(float r[3], const float p[3], const float l1[3], const float l2[3]); diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 5979a24c807..fb9d609b116 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -211,6 +211,21 @@ float dist_to_line_segment_v2(const float v1[2], const float v2[2], const float return sqrtf(rc[0]*rc[0]+ rc[1]*rc[1]); } +/* point closest to v1 on line v2-v3 in 2D */ +void closest_to_line_segment_v2(float *closest, float p[2], float l1[2], float l2[2]) +{ + float lambda, cp[2]; + + lambda= closest_to_line_v2(cp,p, l1, l2); + + if(lambda <= 0.0f) + copy_v2_v2(closest, l1); + else if(lambda >= 1.0f) + copy_v2_v2(closest, l2); + else + copy_v2_v2(closest, cp); +} + /* point closest to v1 on line v2-v3 in 3D */ void closest_to_line_segment_v3(float closest[3], const float v1[3], const float v2[3], const float v3[3]) { diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 93518e993d5..91c1a81707f 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -58,6 +58,7 @@ #include "DNA_cloth_types.h" #include "DNA_controller_types.h" #include "DNA_constraint_types.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_effect_types.h" #include "DNA_fileglobal_types.h" #include "DNA_genfile.h" @@ -4030,6 +4031,33 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) } } + else if (md->type==eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData*) md; + + pmd->baking = 0; /* just in case (should be 0 already) */ + + if(pmd->type==MOD_DYNAMICPAINT_TYPE_CANVAS) + { + pmd->paint = NULL; + + pmd->canvas = newdataadr(fd, pmd->canvas); + pmd->canvas->pmd = pmd; + pmd->canvas->surface = NULL; + pmd->canvas->dm = NULL; + pmd->canvas->ui_info[0] = '\0'; + } + else if(pmd->type==MOD_DYNAMICPAINT_TYPE_PAINT) + { + pmd->canvas = NULL; + + pmd->paint = newdataadr(fd, pmd->paint); + pmd->paint->pmd = pmd; + pmd->paint->mat = newdataadr(fd, pmd->paint->mat); + pmd->paint->psys = newdataadr(fd, pmd->paint->psys); + pmd->paint->paint_ramp = newdataadr(fd, pmd->paint->paint_ramp); + pmd->paint->dm = NULL; + } + } else if (md->type==eModifierType_Collision) { CollisionModifierData *collmd = (CollisionModifierData*) md; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 17f54141252..b1e86aebe38 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -99,6 +99,7 @@ Any case: direct data is ALWAYS after the lib block #include "DNA_cloth_types.h" #include "DNA_constraint_types.h" #include "DNA_controller_types.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_genfile.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" @@ -1250,6 +1251,20 @@ static void write_modifiers(WriteData *wd, ListBase *modbase) FluidsimModifierData *fluidmd = (FluidsimModifierData*) md; writestruct(wd, DATA, "FluidsimSettings", 1, fluidmd->fss); + } + else if(md->type==eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData*) md; + + if(pmd->type & MOD_DYNAMICPAINT_TYPE_CANVAS) + { + writestruct(wd, DATA, "DynamicPaintCanvasSettings", 1, pmd->canvas); + } + else if(pmd->type & MOD_DYNAMICPAINT_TYPE_PAINT) + { + writestruct(wd, DATA, "DynamicPaintPainterSettings", 1, pmd->paint); + writestruct(wd, DATA, "Material", 1, pmd->paint->mat); + writestruct(wd, DATA, "ColorBand", 1, pmd->paint->paint_ramp); + } } else if (md->type==eModifierType_Collision) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index af5a69da653..3f339541793 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -741,7 +741,7 @@ static uiLayout *draw_modifier(uiLayout *layout, Scene *scene, Object *ob, Modif uiBlockBeginAlign(block); /* Softbody not allowed in this situation, enforce! */ if ( ((md->type!=eModifierType_Softbody && md->type!=eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) - && (md->type!=eModifierType_Surface) ) + && !ELEM(md->type, eModifierType_Surface, eModifierType_DynamicPaint) ) { uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE); @@ -798,7 +798,7 @@ static uiLayout *draw_modifier(uiLayout *layout, Scene *scene, Object *ob, Modif box= uiLayoutBox(column); row= uiLayoutRow(box, 0); - if (!ELEM(md->type, eModifierType_Collision, eModifierType_Surface)) { + if (!ELEM3(md->type, eModifierType_Collision, eModifierType_Surface, eModifierType_DynamicPaint)) { /* only here obdata, the rest of modifiers is ob level */ uiBlockSetButLock(block, object_data_is_libdata(ob), ERROR_LIBDATA_MESSAGE); diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index 71f1128baf0..22ae03e48df 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -95,6 +95,9 @@ void BOID_OT_state_move_down(struct wmOperatorType *ot); /* physics_fluid.c */ void FLUID_OT_bake(struct wmOperatorType *ot); +/* dynamicpaint.c */ +void DPAINT_OT_bake(struct wmOperatorType *ot); + /* physics_pointcache.c */ void PTCACHE_OT_bake_all(struct wmOperatorType *ot); void PTCACHE_OT_free_bake_all(struct wmOperatorType *ot); diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c index 56bd8b97532..96f88468baa 100644 --- a/source/blender/editors/physics/physics_ops.c +++ b/source/blender/editors/physics/physics_ops.c @@ -166,6 +166,13 @@ static void operatortypes_pointcache(void) WM_operatortype_append(PTCACHE_OT_remove); } +/********************************* dynamic paint ***********************************/ + +static void operatortypes_dynamicpaint(void) +{ + WM_operatortype_append(DPAINT_OT_bake); +} + //static void keymap_pointcache(wmWindowManager *wm) //{ // wmKeyMap *keymap= WM_keymap_find(wm, "Pointcache", 0, 0); @@ -184,6 +191,7 @@ void ED_operatortypes_physics(void) operatortypes_boids(); operatortypes_fluid(); operatortypes_pointcache(); + operatortypes_dynamicpaint(); } void ED_keymap_physics(wmKeyConfig *keyconf) diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index f91e830d52e..8d5a54908f3 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -647,7 +647,7 @@ const char *buttons_context_dir[] = { "world", "object", "mesh", "armature", "lattice", "curve", "meta_ball", "lamp", "camera", "material", "material_slot", "texture", "texture_slot", "bone", "edit_bone", "pose_bone", "particle_system", "particle_system_editable", - "cloth", "soft_body", "fluid", "smoke", "collision", "brush", NULL}; + "cloth", "soft_body", "fluid", "smoke", "collision", "brush", "dynamic_paint", NULL}; int buttons_context(const bContext *C, const char *member, bContextDataResult *result) { @@ -859,6 +859,16 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r set_pointer_type(path, result, &RNA_Brush); return 1; } + else if(CTX_data_equals(member, "dynamic_paint")) { + PointerRNA *ptr= get_pointer_type(path, &RNA_Object); + + if(ptr && ptr->data) { + Object *ob= ptr->data; + ModifierData *md= modifiers_findByType(ob, eModifierType_DynamicPaint); + CTX_data_pointer_set(result, &ob->id, &RNA_DynamicPaintModifier, md); + return 1; + } + } else { return 0; /* not found */ } diff --git a/source/blender/editors/space_outliner/outliner.c b/source/blender/editors/space_outliner/outliner.c index 037b1db56d6..0b6b1b1987b 100644 --- a/source/blender/editors/space_outliner/outliner.c +++ b/source/blender/editors/space_outliner/outliner.c @@ -4420,6 +4420,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto UI_icon_draw(x, y, ICON_MOD_SOLIDIFY); break; case eModifierType_Screw: UI_icon_draw(x, y, ICON_MOD_SCREW); break; + case eModifierType_DynamicPaint: + UI_icon_draw(x, y, ICON_MOD_FLUIDSIM); break; default: UI_icon_draw(x, y, ICON_DOT); break; } diff --git a/source/blender/makesdna/DNA_dynamicpaint_types.h b/source/blender/makesdna/DNA_dynamicpaint_types.h new file mode 100644 index 00000000000..96b673fc70b --- /dev/null +++ b/source/blender/makesdna/DNA_dynamicpaint_types.h @@ -0,0 +1,132 @@ +/** +* ***** 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. +* +* Contributor(s): Miika Hämäläinen +* +* ***** END GPL LICENSE BLOCK ***** +*/ +#ifndef DNA_DYNAMICPAINT_TYPES_H +#define DNA_DYNAMICPAINT_TYPES_H + +struct CurveMapping; + +/* flags */ +#define MOD_DPAINT_ANTIALIAS (1<<0) /* do antialiasing */ +#define MOD_DPAINT_DISSOLVE (1<<1) /* do dissolve */ +#define MOD_DPAINT_FLATTEN (1<<2) /* do displace flatten */ +#define MOD_DPAINT_MULALPHA (1<<3) /* Multiply color by alpha when saving */ + +#define MOD_DPAINT_DRY_LOG (1<<4) /* Use 1/x for drying paint */ + +/* output */ +#define MOD_DPAINT_OUT_PAINT (1<<0) /* output paint texture */ +#define MOD_DPAINT_OUT_WET (1<<1) /* output wetmap */ +#define MOD_DPAINT_OUT_DISP (1<<2) /* output displace texture */ + +/* disp_type */ +#define MOD_DPAINT_DISPFOR_PNG 0 /* displacement output format */ +#define MOD_DPAINT_DISPFOR_OPENEXR 1 /* displacement output format */ + +/* disp_format */ +#define MOD_DPAINT_DISP_DISPLACE 0 /* displacement output displace map */ +#define MOD_DPAINT_DISP_DEPTH 1 /* displacement output depth data */ + +/* effect */ +#define MOD_DPAINT_EFFECT_DO_SPREAD (1<<0) /* do spread effect */ +#define MOD_DPAINT_EFFECT_DO_DRIP (1<<1) /* do spread effect */ +#define MOD_DPAINT_EFFECT_DO_SHRINK (1<<2) /* do spread effect */ + + +/* Canvas settings */ +typedef struct DynamicPaintCanvasSettings { + struct DynamicPaintModifierData *pmd; /* for fast RNA access */ + struct DerivedMesh *dm; + struct PaintSurface *surface; + + int flags; + int output; + short disp_type, disp_format; + int effect; + short effect_ui; // just ui selection box + short pad; // replace if need for another short + + int resolution; + int start_frame, end_frame; + int substeps; + + int dry_speed; + int diss_speed; + float disp_depth; + int dflat_speed; // displace flattening speed + + char paint_output_path[240], wet_output_path[240], displace_output_path[240]; + + float spread_speed, drip_speed, shrink_speed; + char uvlayer_name[32]; + + char ui_info[128]; // UI info text + char error[64]; // Bake error description + +} DynamicPaintCanvasSettings; + + +/* flags */ +#define MOD_DPAINT_PART_RAD (1<<0) /* use particle radius */ +#define MOD_DPAINT_DO_PAINT (1<<1) /* use particle radius */ +#define MOD_DPAINT_DO_WETNESS (1<<2) /* use particle radius */ +#define MOD_DPAINT_DO_DISPLACE (1<<3) /* use particle radius */ + +#define MOD_DPAINT_USE_MATERIAL (1<<4) /* use object material */ +#define MOD_DPAINT_ABS_ALPHA (1<<5) /* doesn't increase alpha unless + paint alpha is higher than existing */ +#define MOD_DPAINT_ERASE (1<<6) /* removes paint */ + +#define MOD_DPAINT_RAMP_ALPHA (1<<7) /* only read falloff ramp alpha */ +#define MOD_DPAINT_EDGE_DISP (1<<8) /* add displacement to intersection edges */ +#define MOD_DPAINT_PROX_FACEALIGNED (1<<9) /* do proximity check only in normal dir */ + +/* collision type */ +#define MOD_DPAINT_COL_VOLUME 1 /* paint with mesh volume */ +#define MOD_DPAINT_COL_DIST 2 /* paint using distance to mesh surface */ +#define MOD_DPAINT_COL_VOLDIST 3 /* use both volume and distance */ +#define MOD_DPAINT_COL_PSYS 4 /* use particle system */ + +/* proximity_falloff */ +#define MOD_DPAINT_PRFALL_SHARP 0 /* no-falloff */ +#define MOD_DPAINT_PRFALL_SMOOTH 1 /* smooth, linear falloff */ +#define MOD_DPAINT_PRFALL_RAMP 2 /* use color ramp */ + + +/* Painter settings */ +typedef struct DynamicPaintPainterSettings { + struct DynamicPaintModifierData *pmd; /* for fast RNA access */ + struct DerivedMesh *dm; + struct ParticleSystem *psys; + struct Material *mat; + + int flags; + int collision; + + float r, g, b, alpha; + float wetness; + + float particle_radius, particle_smooth; + float paint_distance; + float displace_distance, prox_displace_strength; + + // Falloff curves + struct ColorBand *paint_ramp; /* Proximity paint falloff */ + + short proximity_falloff; + short pad; // replace if need for new value + int pad2; // replace if need for new value + + //int pad; +} DynamicPaintPainterSettings; + +#endif diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index d2d8e014015..f7d30a45e6b 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -71,6 +71,7 @@ typedef enum ModifierType { eModifierType_Solidify, eModifierType_Screw, eModifierType_Warp, + eModifierType_DynamicPaint, NUM_MODIFIER_TYPES } ModifierType; @@ -749,6 +750,9 @@ typedef struct ScrewModifierData { typedef struct WarpModifierData { ModifierData modifier; + + + /* keep in sync with MappingInfoModifierData */ struct Tex *texture; struct Object *map_object; @@ -783,5 +787,20 @@ typedef enum { eWarp_Falloff_Sphere = 7, /* PROP_SPHERE */ /* PROP_RANDOM not used */ } WarpModifierFalloff; +/* Dynamic paint modifier flags */ +#define MOD_DYNAMICPAINT_TYPE_CANVAS (1 << 0) +#define MOD_DYNAMICPAINT_TYPE_PAINT (1 << 1) +typedef struct DynamicPaintModifierData { + ModifierData modifier; + + struct DynamicPaintCanvasSettings *canvas; + struct DynamicPaintPainterSettings *paint; + float time; + int type; /* canvas / painter */ + short baking; /* Set nonzero if baking, + * -> updates derived mesh on modifier call*/ + short pad; + int pad2; +} DynamicPaintModifierData; #endif diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 07ee084f976..0508874ed0c 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -132,6 +132,7 @@ const char *includefiles[] = { "DNA_anim_types.h", "DNA_boid_types.h", "DNA_smoke_types.h", + "DNA_dynamicpaint_types.h", // empty string to indicate end of includefiles "" @@ -1183,4 +1184,5 @@ int main(int argc, char ** argv) #include "DNA_anim_types.h" #include "DNA_boid_types.h" #include "DNA_smoke_types.h" +#include "DNA_dynamicpaint_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index a9f7d9f246f..3e0b850486e 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -194,6 +194,9 @@ extern StructRNA RNA_Driver; extern StructRNA RNA_DriverTarget; extern StructRNA RNA_DriverVariable; extern StructRNA RNA_DupliObject; +extern StructRNA RNA_DynamicPaintCanvasSettings; +extern StructRNA RNA_DynamicPaintModifier; +extern StructRNA RNA_DynamicPaintPainterSettings; extern StructRNA RNA_EdgeSplitModifier; extern StructRNA RNA_EditBone; extern StructRNA RNA_EffectSequence; diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index fe6fc91eff4..40c47deecc5 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -2400,6 +2400,7 @@ static RNAProcessItem PROCESS_ITEMS[]= { {"rna_context.c", NULL, RNA_def_context}, {"rna_controller.c", "rna_controller_api.c", RNA_def_controller}, {"rna_curve.c", NULL, RNA_def_curve}, + {"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint}, {"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve}, {"rna_fluidsim.c", NULL, RNA_def_fluidsim}, {"rna_gpencil.c", NULL, RNA_def_gpencil}, diff --git a/source/blender/makesrna/intern/rna_dynamicpaint.c b/source/blender/makesrna/intern/rna_dynamicpaint.c new file mode 100644 index 00000000000..d4ff7c79d93 --- /dev/null +++ b/source/blender/makesrna/intern/rna_dynamicpaint.c @@ -0,0 +1,437 @@ +/** + * ***** 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. + * + * Contributor(s): Miika Hämäläinen + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include + +#include "RNA_define.h" + +#include "rna_internal.h" + +#include "BKE_modifier.h" +#include "BKE_dynamicpaint.h" + +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_dynamicpaint_types.h" + +#include "WM_types.h" + + +#ifdef RNA_RUNTIME + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_particle.h" + + +static char *rna_DynamicPaintCanvasSettings_path(PointerRNA *ptr) +{ + DynamicPaintCanvasSettings *settings = (DynamicPaintCanvasSettings*)ptr->data; + ModifierData *md= (ModifierData *)settings->pmd; + + return BLI_sprintfN("modifiers[\"%s\"].canvas_settings", md->name); +} + +static char *rna_DynamicPaintPainterSettings_path(PointerRNA *ptr) +{ + DynamicPaintPainterSettings *settings = (DynamicPaintPainterSettings*)ptr->data; + ModifierData *md= (ModifierData *)settings->pmd; + + return BLI_sprintfN("modifiers[\"%s\"].paint_settings", md->name); +} + +static void rna_DynamicPaint_uvlayer_set(PointerRNA *ptr, const char *value) +{ + DynamicPaintCanvasSettings *canvas= (DynamicPaintCanvasSettings*)ptr->data; + rna_object_uvlayer_name_set(ptr, value, canvas->uvlayer_name, sizeof(canvas->uvlayer_name)); +} + + +#else + +static void rna_def_dynamic_paint_canvas_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* + * Effect type + * Only used by ui to view per effect settings + */ + static EnumPropertyItem prop_dynamicpaint_effecttype[] = { + {1, "SPREAD", 0, "Spread", ""}, + {2, "DRIP", 0, "Drip", ""}, + {3, "SHRINK", 0, "Shrink", ""}, + {0, NULL, 0, NULL, NULL}}; + + /* + * Displacemap file format + */ + static EnumPropertyItem prop_dynamicpaint_disp_format[] = { + {MOD_DPAINT_DISPFOR_PNG, "PNG", 0, "PNG", ""}, +#ifdef WITH_OPENEXR + {MOD_DPAINT_DISPFOR_OPENEXR, "OPENEXR", 0, "OpenEXR", ""}, +#endif + {0, NULL, 0, NULL, NULL}}; + + /* + * Displacemap type + */ + static EnumPropertyItem prop_dynamicpaint_disp_type[] = { + {MOD_DPAINT_DISP_DISPLACE, "DISPLACE", 0, "Displacement", ""}, + {MOD_DPAINT_DISP_DEPTH, "DEPTH", 0, "Depth", ""}, + {0, NULL, 0, NULL, NULL}}; + + srna = RNA_def_struct(brna, "DynamicPaintCanvasSettings", NULL); + RNA_def_struct_ui_text(srna, "Canvas Settings", "Dynamic Paint canvas settings"); + RNA_def_struct_sdna(srna, "DynamicPaintCanvasSettings"); + RNA_def_struct_path_func(srna, "rna_DynamicPaintCanvasSettings_path"); + + /* + * Paint, wet and disp + */ + prop= RNA_def_property(srna, "use_dissolve_paint", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_DISSOLVE); + RNA_def_property_ui_text(prop, "Dissolve Paint", "Enable paint to disappear over time."); + + prop= RNA_def_property(srna, "dissolve_speed", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "diss_speed"); + RNA_def_property_range(prop, 1.0, 10000.0); + RNA_def_property_ui_range(prop, 1.0, 10000.0, 5, 0); + RNA_def_property_ui_text(prop, "Dissolve Speed", "Dissolve Speed"); + + prop= RNA_def_property(srna, "use_flatten_disp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_FLATTEN); + RNA_def_property_ui_text(prop, "Time Flatten", "Makes displacement map to flatten over time."); + + prop= RNA_def_property(srna, "flatten_speed", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "dflat_speed"); + RNA_def_property_range(prop, 1.0, 10000.0); + RNA_def_property_ui_range(prop, 1.0, 10000.0, 5, 0); + RNA_def_property_ui_text(prop, "Flatten Speed", "Flatten Speed"); + + prop= RNA_def_property(srna, "dry_speed", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "dry_speed"); + RNA_def_property_range(prop, 1.0, 10000.0); + RNA_def_property_ui_range(prop, 1.0, 10000.0, 5, 0); + RNA_def_property_ui_text(prop, "Dry Speed", "Dry Speed"); + + /* + * Simulation settings + */ + prop= RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_sdna(prop, NULL, "resolution"); + RNA_def_property_range(prop, 16.0, 4096.0); + RNA_def_property_ui_range(prop, 16.0, 4096.0, 1, 0); + RNA_def_property_ui_text(prop, "Resolution", "Texture resolution"); + + prop= RNA_def_property(srna, "uv_layer", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "uvlayer_name"); + RNA_def_property_ui_text(prop, "UV Layer", "UV layer name"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_DynamicPaint_uvlayer_set"); + + prop= RNA_def_property(srna, "start_frame", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_sdna(prop, NULL, "start_frame"); + RNA_def_property_range(prop, 1.0, 9999.0); + RNA_def_property_ui_range(prop, 1.0, 9999, 1, 0); + RNA_def_property_ui_text(prop, "Start Frame", "Simulation start frame"); + + prop= RNA_def_property(srna, "end_frame", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_sdna(prop, NULL, "end_frame"); + RNA_def_property_range(prop, 1.0, 9999.0); + RNA_def_property_ui_range(prop, 1.0, 9999.0, 1, 0); + RNA_def_property_ui_text(prop, "End Frame", "Simulation end frame"); + + prop= RNA_def_property(srna, "substeps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "substeps"); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_range(prop, 0.0, 10, 1, 0); + RNA_def_property_ui_text(prop, "Sub-Steps", "Do extra frames between scene frames to ensure smooth motion."); + + prop= RNA_def_property(srna, "use_anti_aliasing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_ANTIALIAS); + RNA_def_property_ui_text(prop, "Anti-aliasing", "Uses 5x multisampling to smoothen paint edges."); + + prop= RNA_def_property(srna, "ui_info", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "ui_info"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Bake Info", "Info on bake status"); + + /* + * Effect Settings + */ + prop= RNA_def_property(srna, "effect_ui", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_sdna(prop, NULL, "effect_ui"); + RNA_def_property_enum_items(prop, prop_dynamicpaint_effecttype); + RNA_def_property_ui_text(prop, "Effect Type", ""); + + prop= RNA_def_property(srna, "use_dry_log", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_DRY_LOG); + RNA_def_property_ui_text(prop, "Slow", "Use 1/x instead of linear drying."); + + prop= RNA_def_property(srna, "use_spread", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "effect", MOD_DPAINT_EFFECT_DO_SPREAD); + RNA_def_property_ui_text(prop, "Use Spread", "Processes spread effect. Spreads wet paint around surface."); + + prop= RNA_def_property(srna, "spread_speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "spread_speed"); + RNA_def_property_range(prop, 0.1, 5.0); + RNA_def_property_ui_range(prop, 0.1, 5.0, 1, 2); + RNA_def_property_ui_text(prop, "Spread Speed", "How fast spread effect moves on the canvas surface."); + + prop= RNA_def_property(srna, "use_drip", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "effect", MOD_DPAINT_EFFECT_DO_DRIP); + RNA_def_property_ui_text(prop, "Use Drip", "Processes drip effect. Drips wet paint to gravity direction."); + + prop= RNA_def_property(srna, "drip_speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "drip_speed"); + RNA_def_property_range(prop, 0.1, 5.0); + RNA_def_property_ui_range(prop, 0.1, 5.0, 1, 2); + RNA_def_property_ui_text(prop, "Drip Speed", "How fast drip effect moves on the canvas surface."); + + prop= RNA_def_property(srna, "use_shrink", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "effect", MOD_DPAINT_EFFECT_DO_SHRINK); + RNA_def_property_ui_text(prop, "Use Shrink", "Processes shrink effect. Shrinks paint areas."); + + prop= RNA_def_property(srna, "shrink_speed", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "shrink_speed"); + RNA_def_property_range(prop, 0.1, 5.0); + RNA_def_property_ui_range(prop, 0.1, 5.0, 1, 2); + RNA_def_property_ui_text(prop, "Shrink Speed", "How fast shrink effect moves on the canvas surface."); + + /* + * Output settings + */ + prop= RNA_def_property(srna, "premultiply", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_MULALPHA); + RNA_def_property_ui_text(prop, "Premultiply alpha", "Multiplies color by alpha. (Recommended for Blender input.)"); + + prop= RNA_def_property(srna, "paint_output_path", PROP_STRING, PROP_DIRPATH); + RNA_def_property_string_sdna(prop, NULL, "paint_output_path"); + RNA_def_property_ui_text(prop, "Output Path", "Directory/name to save color textures"); + + prop= RNA_def_property(srna, "wet_output_path", PROP_STRING, PROP_DIRPATH); + RNA_def_property_string_sdna(prop, NULL, "wet_output_path"); + RNA_def_property_ui_text(prop, "Output Path", "Directory/name to save wetmap textures"); + + prop= RNA_def_property(srna, "displace_output_path", PROP_STRING, PROP_DIRPATH); + RNA_def_property_string_sdna(prop, NULL, "displace_output_path"); + RNA_def_property_ui_text(prop, "Output Path", "Directory/name to save displace textures"); + + prop= RNA_def_property(srna, "output_paint", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "output", MOD_DPAINT_OUT_PAINT); + RNA_def_property_ui_text(prop, "Ouput Paintmaps", "Generates paint textures."); + + prop= RNA_def_property(srna, "output_wet", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "output", MOD_DPAINT_OUT_WET); + RNA_def_property_ui_text(prop, "Ouput Wetmaps", "Generates wetmaps."); + + prop= RNA_def_property(srna, "output_disp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "output", MOD_DPAINT_OUT_DISP); + RNA_def_property_ui_text(prop, "Output Displacement", "Generates displacement textures."); + + prop= RNA_def_property(srna, "displacement", PROP_FLOAT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_float_sdna(prop, NULL, "disp_depth"); + RNA_def_property_range(prop, 0.01, 5.0); + RNA_def_property_ui_range(prop, 0.01, 5.0, 1, 2); + RNA_def_property_ui_text(prop, "Displace Strength", "Maximum level of intersection to store in the texture. Use same value as the displace method strength."); + + prop= RNA_def_property(srna, "disp_format", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_sdna(prop, NULL, "disp_format"); + RNA_def_property_enum_items(prop, prop_dynamicpaint_disp_format); + RNA_def_property_ui_text(prop, "File Format", ""); + + prop= RNA_def_property(srna, "disp_type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_sdna(prop, NULL, "disp_type"); + RNA_def_property_enum_items(prop, prop_dynamicpaint_disp_type); + RNA_def_property_ui_text(prop, "Data Type", ""); +} + +static void rna_def_dynamic_paint_painter_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* paint collision type */ + static EnumPropertyItem prop_dynamicpaint_collisiontype[] = { + {MOD_DPAINT_COL_PSYS, "PSYS", 0, "Particle System", ""}, + {MOD_DPAINT_COL_DIST, "DISTANCE", 0, "Proximity", ""}, + {MOD_DPAINT_COL_VOLDIST, "VOLDIST", 0, "Mesh Volume + Proximity", ""}, + {MOD_DPAINT_COL_VOLUME, "VOLUME", 0, "Mesh Volume", ""}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem prop_dynamicpaint_prox_falloff[] = { + {MOD_DPAINT_PRFALL_SMOOTH, "SMOOTH", 0, "Smooth", ""}, + {MOD_DPAINT_PRFALL_SHARP, "SHARP", 0, "Sharp", ""}, + {MOD_DPAINT_PRFALL_RAMP, "RAMP", 0, "Color Ramp", ""}, + {0, NULL, 0, NULL, NULL}}; + + srna = RNA_def_struct(brna, "DynamicPaintPainterSettings", NULL); + RNA_def_struct_ui_text(srna, "Paint Settings", "Paint settings"); + RNA_def_struct_sdna(srna, "DynamicPaintPainterSettings"); + RNA_def_struct_path_func(srna, "rna_DynamicPaintPainterSettings_path"); + + /* + * Paint + */ + prop= RNA_def_property(srna, "paint_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "r"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Paint Color", "Color of the paint."); + RNA_def_property_update(prop, NC_MATERIAL|ND_SHADING_DRAW, NULL); + + prop= RNA_def_property(srna, "paint_alpha", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "alpha"); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_range(prop, 0.0, 10.0, 1, 2); + RNA_def_property_ui_text(prop, "Paint Alpha", "Paint alpha."); + + prop= RNA_def_property(srna, "use_material", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_USE_MATERIAL); + RNA_def_property_ui_text(prop, "Use object material", "Use object material to define color and alpha."); + + prop= RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "mat"); + RNA_def_property_ui_text(prop, "Material", "Material to use. If not defined, material linked to the mesh is used."); + RNA_def_property_flag(prop, PROP_EDITABLE); + + prop= RNA_def_property(srna, "absolute_alpha", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_ABS_ALPHA); + RNA_def_property_ui_text(prop, "Absolute Alpha", "Only increase alpha value if paint alpha is higher than existing."); + + prop= RNA_def_property(srna, "paint_wetness", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "wetness"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 5, 3); + RNA_def_property_ui_text(prop, "Paint Wetness", "Paint Wetness. Visible in wet map. Some effects only affect wet paint."); + + prop= RNA_def_property(srna, "paint_erase", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_ERASE); + RNA_def_property_ui_text(prop, "Erase Paint", "Erase / remove paint instead of adding it."); + + /* + * Paint Area / Collision + */ + prop= RNA_def_property(srna, "paint_source", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_sdna(prop, NULL, "collision"); + RNA_def_property_enum_items(prop, prop_dynamicpaint_collisiontype); + RNA_def_property_ui_text(prop, "Paint Source", ""); + + prop= RNA_def_property(srna, "paint_distance", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "paint_distance"); + RNA_def_property_range(prop, 0.0, 500.0); + RNA_def_property_ui_range(prop, 0.0, 500.0, 10, 3); + RNA_def_property_ui_text(prop, "Proximity Distance", "Maximum distance to mesh surface to affect paint."); + + prop= RNA_def_property(srna, "prox_ramp_alpha", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_RAMP_ALPHA); + RNA_def_property_ui_text(prop, "Only Use Alpha", "Only reads color ramp alpha."); + + prop= RNA_def_property(srna, "edge_displace", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_EDGE_DISP); + RNA_def_property_ui_text(prop, "Edge Displace", "Add displacement to intersection edges too."); + + prop= RNA_def_property(srna, "displace_distance", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "displace_distance"); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_range(prop, 0.0, 10.0, 5, 3); + RNA_def_property_ui_text(prop, "Displace Distance", "Maximum distance to mesh surface to displace."); + + prop= RNA_def_property(srna, "prox_displace_strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "prox_displace_strength"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 5, 3); + RNA_def_property_ui_text(prop, "Strength", "How much of maximum intersection will be used in edges."); + + prop= RNA_def_property(srna, "prox_falloff", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_sdna(prop, NULL, "proximity_falloff"); + RNA_def_property_enum_items(prop, prop_dynamicpaint_prox_falloff); + RNA_def_property_ui_text(prop, "Paint Falloff", ""); + + prop= RNA_def_property(srna, "prox_facealigned", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_PROX_FACEALIGNED); + RNA_def_property_ui_text(prop, "Face Aligned", "Check proximity in face normal direction only."); + + + /* + * Particle + */ + prop= RNA_def_property(srna, "psys", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "psys"); + RNA_def_property_struct_type(prop, "ParticleSystem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Particle Systems", "The particle system to paint with."); + + prop= RNA_def_property(srna, "use_part_radius", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_PART_RAD); + RNA_def_property_ui_text(prop, "Use Particle Radius", "Uses radius from particle settings."); + + prop= RNA_def_property(srna, "solid_radius", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "particle_radius"); + RNA_def_property_range(prop, 0.01, 10.0); + RNA_def_property_ui_range(prop, 0.01, 2.0, 5, 3); + RNA_def_property_ui_text(prop, "Solid Radius", "Radius that will be painted solid."); + + prop= RNA_def_property(srna, "smooth_radius", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "particle_smooth"); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 5, 0); + RNA_def_property_ui_text(prop, "Smooth Radius", "Smooth falloff added after solid paint area."); + + /* + * Effect + */ + prop= RNA_def_property(srna, "do_paint", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_DO_PAINT); + RNA_def_property_ui_text(prop, "Affect Paint", "Makes this painter to affect paint data."); + + prop= RNA_def_property(srna, "do_wet", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_DO_WETNESS); + RNA_def_property_ui_text(prop, "Affect Wetness", "Makes this painter to affect wetness data."); + + prop= RNA_def_property(srna, "do_displace", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_DPAINT_DO_DISPLACE); + RNA_def_property_ui_text(prop, "Affect Displace", "Makes this painter to affect displace data."); + + + /* + * Color ramps + */ + prop= RNA_def_property(srna, "paint_ramp", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "paint_ramp"); + RNA_def_property_struct_type(prop, "ColorRamp"); + RNA_def_property_ui_text(prop, "Paint Color Ramp", "Color ramp used to define proximity falloff."); +} + +void RNA_def_dynamic_paint(BlenderRNA *brna) +{ + rna_def_dynamic_paint_canvas_settings(brna); + rna_def_dynamic_paint_painter_settings(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index a9fb545ec3f..ae3dd260f64 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -138,6 +138,7 @@ void RNA_def_constraint(struct BlenderRNA *brna); void RNA_def_context(struct BlenderRNA *brna); void RNA_def_controller(struct BlenderRNA *brna); void RNA_def_curve(struct BlenderRNA *brna); +void RNA_def_dynamic_paint(struct BlenderRNA *brna); void RNA_def_fluidsim(struct BlenderRNA *brna); void RNA_def_fcurve(struct BlenderRNA *brna); void RNA_def_gameproperty(struct BlenderRNA *brna); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 333451e1d31..b1d3f467715 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -47,6 +47,7 @@ #include "BKE_animsys.h" #include "BKE_bmesh.h" /* For BevelModifierData */ +#include "BKE_dynamicpaint.h" /* For dynamicPaint_Modifier_free & *_createType */ #include "BKE_multires.h" #include "BKE_smoke.h" /* For smokeModifier_free & smokeModifier_createType */ @@ -91,6 +92,7 @@ EnumPropertyItem modifier_type_items[] ={ {eModifierType_Smoke, "SMOKE", ICON_MOD_SMOKE, "Smoke", ""}, {eModifierType_Softbody, "SOFT_BODY", ICON_MOD_SOFT, "Soft Body", ""}, {eModifierType_Surface, "SURFACE", ICON_MOD_PHYSICS, "Surface", ""}, + {eModifierType_DynamicPaint, "DYNAMIC_PAINT", ICON_MOD_FLUIDSIM, "Dynamic Paint", ""}, {0, NULL, 0, NULL, NULL}}; #ifdef RNA_RUNTIME @@ -183,6 +185,8 @@ static StructRNA* rna_Modifier_refine(struct PointerRNA *ptr) return &RNA_ScrewModifier; case eModifierType_Warp: return &RNA_WarpModifier; + case eModifierType_DynamicPaint: + return &RNA_DynamicPaintModifier; default: return &RNA_Modifier; } @@ -279,6 +283,21 @@ static void rna_Smoke_set_type(Main *bmain, Scene *scene, PointerRNA *ptr) rna_Modifier_dependency_update(bmain, scene, ptr); } +static void rna_DynamicPaint_set_type(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + DynamicPaintModifierData *pmd= (DynamicPaintModifierData *)ptr->data; + + // nothing changed + if((pmd->type & MOD_DYNAMICPAINT_TYPE_CANVAS) && pmd->canvas) + return; + + dynamicPaint_Modifier_free(pmd); + dynamicPaint_Modifier_createType(pmd); // create regarding of selected type + + // update dependancy + rna_Modifier_dependency_update(bmain, scene, ptr); +} + static void rna_ExplodeModifier_vgroup_get(PointerRNA *ptr, char *value) { ExplodeModifierData *emd= (ExplodeModifierData*)ptr->data; @@ -1902,6 +1921,38 @@ static void rna_def_modifier_smoke(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Smoke_set_type"); } +static void rna_def_modifier_dynamic_paint(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem prop_dynamicpaint_type_items[] = { + {0, "NONE", 0, "None", ""}, + {MOD_DYNAMICPAINT_TYPE_CANVAS, "CANVAS", 0, "Canvas", ""}, + {MOD_DYNAMICPAINT_TYPE_PAINT, "PAINT", 0, "Paint", ""}, + {0, NULL, 0, NULL, NULL}}; + + srna= RNA_def_struct(brna, "DynamicPaintModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Dynamic Paint Modifier", "Dynamic Paint modifier"); + RNA_def_struct_sdna(srna, "DynamicPaintModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_FLUIDSIM); + + prop= RNA_def_property(srna, "canvas_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "canvas"); + RNA_def_property_ui_text(prop, "Canvas Settings", ""); + + prop= RNA_def_property(srna, "paint_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "paint"); + RNA_def_property_ui_text(prop, "Paint Settings", ""); + + prop= RNA_def_property(srna, "dynamicpaint_type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, prop_dynamicpaint_type_items); + RNA_def_property_ui_text(prop, "Type", ""); + RNA_def_property_update(prop, 0, "rna_DynamicPaint_set_type"); +} + static void rna_def_modifier_collision(BlenderRNA *brna) { StructRNA *srna; @@ -2462,6 +2513,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_smoke(brna); rna_def_modifier_solidify(brna); rna_def_modifier_screw(brna); + rna_def_modifier_dynamic_paint(brna); } #endif diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 7abcb331f08..c9243b29451 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRC intern/MOD_curve.c intern/MOD_decimate.c intern/MOD_displace.c + intern/MOD_dynamicpaint.c intern/MOD_edgesplit.c intern/MOD_explode.c intern/MOD_fluidsim.c diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index 4e44a226c64..4e10c1711f5 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -72,6 +72,7 @@ extern ModifierTypeInfo modifierType_ShapeKey; extern ModifierTypeInfo modifierType_Solidify; extern ModifierTypeInfo modifierType_Screw; extern ModifierTypeInfo modifierType_Warp; +extern ModifierTypeInfo modifierType_DynamicPaint; /* MOD_util.c */ void modifier_type_init(ModifierTypeInfo *types[]); diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c new file mode 100644 index 00000000000..66038db47a8 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -0,0 +1,109 @@ +/* +* ***** 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. +* +* Contributor(s): Miika Hämäläinen +* +* ***** END GPL LICENSE BLOCK ***** +* +*/ + +#include "stddef.h" + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_modifier.h" +#include "BKE_dynamicpaint.h" + +#include "depsgraph_private.h" + +#include "MOD_util.h" + + +static void initData(ModifierData *md) +{ + DynamicPaintModifierData *pmd = (DynamicPaintModifierData*) md; + + pmd->canvas = NULL; + pmd->paint = NULL; + pmd->baking = 0; + pmd->time = -1; + pmd->type = 0; +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + DynamicPaintModifierData *pmd = (DynamicPaintModifierData*)md; + DynamicPaintModifierData *tpmd = (DynamicPaintModifierData*)target; + + dynamicPaint_Modifier_copy(pmd, tpmd); +} + +static void freeData(ModifierData *md) +{ + DynamicPaintModifierData *pmd = (DynamicPaintModifierData*) md; + + dynamicPaint_Modifier_free (pmd); +} + +static CustomDataMask requiredDataMask(Object *ob, ModifierData *md) +{ + CustomDataMask dataMask = 0; + + dataMask |= (1 << CD_MTFACE); + + return dataMask; +} + +static void deformVerts( + ModifierData *md, Object *ob, DerivedMesh *derivedData, + float (*vertexCos)[3], int numVerts, int useRenderParams, int isFinalCalc) +{ + DynamicPaintModifierData *pmd = (DynamicPaintModifierData*) md; + DerivedMesh *dm = get_cddm(ob, NULL, derivedData, vertexCos); + + + dynamicPaint_Modifier_do(pmd, md->scene, ob, dm); + + if(dm != derivedData) + dm->release(dm); +} + +static int dependsOnTime(ModifierData *md) +{ + return 1; +} + + +ModifierTypeInfo modifierType_DynamicPaint = { + /* name */ "Dynamic Paint", + /* structName */ "DynamicPaintModifierData", + /* structSize */ sizeof(DynamicPaintModifierData), + /* type */ eModifierTypeType_OnlyDeform, + /* flags */ eModifierTypeFlag_AcceptsMesh + | eModifierTypeFlag_Single, + + /* copyData */ copyData, + /* deformVerts */ deformVerts, + /* deformMatrices */ 0, + /* deformVertsEM */ 0, + /* deformMatricesEM */ 0, + /* applyModifier */ 0, + /* applyModifierEM */ 0, + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ freeData, + /* isDisabled */ 0, + /* updateDepgraph */ 0, + /* dependsOnTime */ dependsOnTime, + /* foreachObjectLink */ 0, + /* foreachIDLink */ 0, +}; diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 9fe37e2d174..7ce928679d1 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -279,5 +279,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(Solidify); INIT_TYPE(Screw); INIT_TYPE(Warp); + INIT_TYPE(DynamicPaint); #undef INIT_TYPE } diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index 958c19ab9ca..74fc161f262 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -201,6 +201,8 @@ int multitex_ext_safe(struct Tex *tex, float *texvec, struct TexResult *texres); int multitex_nodes(struct Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, struct TexResult *texres, short thread, short which_output, struct ShadeInput *shi, struct MTex *mtex); +void texco_mapping_ext(float *facenor, struct Tex* tex, struct MTex* mtex, float* co, float* dx, float* dy, float* texvec); + /* shaded view and bake */ struct Render; struct Image; diff --git a/source/blender/render/intern/include/voxeldata.h b/source/blender/render/intern/include/voxeldata.h index e9991a2a19c..2353b24f310 100644 --- a/source/blender/render/intern/include/voxeldata.h +++ b/source/blender/render/intern/include/voxeldata.h @@ -43,6 +43,7 @@ typedef struct VoxelDataHeader int frames; } VoxelDataHeader; +void cache_voxeldata(Tex *tex, int scene_frame); void make_voxeldata(struct Render *re); void free_voxeldata(struct Render *re); int voxeldatatex(struct Tex *tex, float *texvec, struct TexResult *texres); diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index 5f5dab94ba3..1c84f9f0e01 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -1672,6 +1672,21 @@ static void texco_mapping(ShadeInput* shi, Tex* tex, MTex* mtex, float* co, floa } } +void texco_mapping_ext(float *facenor, Tex* tex, MTex* mtex, float* co, float* dx, float* dy, float* texvec) +{ + ShadeInput dum_shi; + float null_v3[3] = {0.0f, 0.0f, 0.0f}; + memset (&dum_shi,0,sizeof(ShadeInput)); + + // Make sure values used by texco_mapping() are correct + dum_shi.osatex = NULL; + dum_shi.vlr = NULL; + VECCOPY(dum_shi.facenor, facenor); + + texco_mapping(&dum_shi, tex, mtex, co, dx, dy, texvec, null_v3, null_v3); +} + + /* Bump code from 2.5 development cycle, has a number of bugs, but here for compatibility */ typedef struct CompatibleBump { diff --git a/source/blender/render/intern/source/voxeldata.c b/source/blender/render/intern/source/voxeldata.c index b8cb5c21337..511c03b424e 100644 --- a/source/blender/render/intern/source/voxeldata.c +++ b/source/blender/render/intern/source/voxeldata.c @@ -303,7 +303,7 @@ static void init_frame_smoke(VoxelData *vd, float cfra) return; } -static void cache_voxeldata(struct Render *re, Tex *tex) +void cache_voxeldata(Tex *tex, int scene_frame) { VoxelData *vd = tex->vd; FILE *fp; @@ -311,7 +311,7 @@ static void cache_voxeldata(struct Render *re, Tex *tex) char path[sizeof(vd->source_path)]; /* only re-cache if dataset needs updating */ - if ((vd->flag & TEX_VD_STILL) || (vd->cachedframe == re->r.cfra)) + if ((vd->flag & TEX_VD_STILL) || (vd->cachedframe == scene_frame)) if (vd->ok) return; /* clear out old cache, ready for new */ @@ -323,7 +323,7 @@ static void cache_voxeldata(struct Render *re, Tex *tex) if (vd->flag & TEX_VD_STILL) curframe = vd->still_frame; else - curframe = re->r.cfra; + curframe = scene_frame; BLI_strncpy(path, vd->source_path, sizeof(path)); @@ -332,7 +332,7 @@ static void cache_voxeldata(struct Render *re, Tex *tex) load_frame_image_sequence(vd, tex); return; case TEX_VD_SMOKE: - init_frame_smoke(vd, re->r.cfra); + init_frame_smoke(vd, scene_frame); return; case TEX_VD_BLENDERVOXEL: BLI_path_abs(path, G.main->name); @@ -367,7 +367,7 @@ void make_voxeldata(struct Render *re) /* XXX: should be doing only textures used in this render */ for (tex= re->main->tex.first; tex; tex= tex->id.next) { if(tex->id.us && tex->type==TEX_VOXELDATA) { - cache_voxeldata(re, tex); + cache_voxeldata(tex, re->r.cfra); } } diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index cf2481bc982..4daa0aa8d1f 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -121,6 +121,7 @@ int multitex_thread(struct Tex *tex, float *texvec, float *dxt, float *dyt, int int multitex_ext(struct Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, struct TexResult *texres){return 0;} int multitex_ext_safe(struct Tex *tex, float *texvec, struct TexResult *texres){return 0;} int multitex_nodes(struct Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, struct TexResult *texres, short thread, short which_output, struct ShadeInput *shi, struct MTex *mtex) {return 0;} +void texco_mapping_ext(float *facenor, struct Tex* tex, struct MTex* mtex, float* co, float* dx, float* dy, float* texvec){} /* nodes */ struct RenderResult *RE_GetResult(struct Render *re){return (struct RenderResult *) NULL;} @@ -145,9 +146,12 @@ double elbeemEstimateMemreq(int res, float sx, float sy, float sz, int refine, c struct Render *RE_NewRender(const char *name){return (struct Render*) NULL;} void RE_SwapResult(struct Render *re, struct RenderResult **rr){} void RE_BlenderFrame(struct Render *re, struct Scene *scene, int frame){} +void cache_voxeldata(struct Tex *tex, int scene_frame){} /* rna */ float *give_cursor(struct Scene *scene, struct View3D *v3d){return (float *) NULL;} +void WM_timecursor(struct wmWindow *win, int nr){} +void WM_cursor_restore(struct wmWindow *win){} void WM_menutype_free(void){} void WM_menutype_freelink(struct MenuType* mt){} int WM_menutype_add(struct MenuType *mt) {return 0;} @@ -174,11 +178,13 @@ void ED_armature_edit_bone_remove(struct bArmature *arm, struct EditBone *exBone void object_test_constraints (struct Object *owner){} void ED_object_parent(struct Object *ob, struct Object *par, int type, const char *substr){} void ED_object_constraint_set_active(struct Object *ob, struct bConstraint *con){} +int ED_operator_object_active_editable(struct bContext *C){return 0;} void ED_node_composit_default(struct Scene *sce){} void *ED_region_draw_cb_activate(struct ARegionType *art, void(*draw)(const struct bContext *, struct ARegion *, void *), void *custumdata, int type){return 0;} /* XXX this one looks weird */ void *ED_region_draw_cb_customdata(void *handle){return 0;} /* XXX This one looks wrong also */ void ED_region_draw_cb_exit(struct ARegionType *art, void *handle){} void ED_area_headerprint(struct ScrArea *sa, char *str){} +void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, struct bScreen *screen, int mute){} struct EditBone *ED_armature_bone_get_mirrored(struct ListBase *edbo, struct EditBone *ebo){return (struct EditBone *) NULL;} struct EditBone *ED_armature_edit_bone_add(struct bArmature *arm, char *name){return (struct EditBone*) NULL;}