diff options
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_bvhutils.h | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_dynamicpaint.h | 76 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_pointcache.h | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/DerivedMesh.c | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/bvhutils.c | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/cdderivedmesh.c | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/dynamicpaint.c | 4871 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/particle.c | 9 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/pointcache.c | 164 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subsurf_ccg.c | 6 |
11 files changed, 5138 insertions, 14 deletions
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..c49a13f7d0e --- /dev/null +++ b/source/blender/blenkernel/BKE_dynamicpaint.h @@ -0,0 +1,76 @@ +/** + * ***** 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_ + +#include "DNA_dynamicpaint_types.h" + +struct PaintEffectData; +struct PaintBakeData; + +/* Actual surface point */ +typedef struct PaintSurfaceData { + /* surface format data */ + void *format_data; + /* surface type data */ + void *type_data; + /* point neighbor data */ + struct PaintAdjData *adj_data; + + struct PaintBakeData *bData; + unsigned int total_points; + +} PaintSurfaceData; + +/* Paint type surface point */ +typedef struct PaintPoint { + + /* 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; /* -1 = doesn't exist (On UV mapped image + * there can be points that doesn't exist on mesh surface) + * 0 = empty or dry + * 1 = wet paint + * 2 = new paint */ + float color[3]; + float alpha; +} PaintPoint; + +/* heigh field waves */ +typedef struct PaintWavePoint { + + float height; + float velocity; + short state; /* 0 = neutral + * 1 = obstacle + * 2 = reflect only */ + float foam; + +} PaintWavePoint; + +struct DerivedMesh *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_copy(struct DynamicPaintModifierData *pmd, struct DynamicPaintModifierData *tsmd); + +void dynamicPaint_cacheUpdateFrames(struct DynamicPaintSurface *surface); +void dynamicPaint_clearSurface(DynamicPaintSurface *surface); +int dynamicPaint_resetSurface(struct DynamicPaintSurface *surface); +int dynamicPaint_surfaceHasColorPreview(DynamicPaintSurface *surface); +void dynamicPaintSurface_updateType(struct DynamicPaintSurface *surface); +void dynamicPaintSurface_setUniqueName(DynamicPaintSurface *surface, char *basename); + +#endif /* BKE_DYNAMIC_PAINT_H_ */ diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 346368a5958..b7f60af38a6 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -34,6 +34,7 @@ */ #include "DNA_ID.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_object_force.h" #include "DNA_boid_types.h" #include <stdio.h> /* for FILE */ @@ -66,6 +67,7 @@ #define PTCACHE_TYPE_CLOTH 2 #define PTCACHE_TYPE_SMOKE_DOMAIN 3 #define PTCACHE_TYPE_SMOKE_HIGHRES 4 +#define PTCACHE_TYPE_DYNAMICPAINT 5 /* high bits reserved for flags that need to be stored in file */ #define PTCACHE_TYPEFLAG_COMPRESS (1<<16) @@ -139,7 +141,7 @@ typedef struct PTCacheID { /* copies point data to cache data */ int (*write_stream)(PTCacheFile *pf, void *calldata); /* copies cache cata to point data */ - void (*read_stream)(PTCacheFile *pf, void *calldata); + int (*read_stream)(PTCacheFile *pf, void *calldata); /* copies custom extradata to cache data */ void (*write_extra_data)(void *calldata, struct PTCacheMem *pm, int cfra); @@ -255,6 +257,7 @@ void BKE_ptcache_id_from_softbody(PTCacheID *pid, struct Object *ob, struct Soft void BKE_ptcache_id_from_particles(PTCacheID *pid, struct Object *ob, struct ParticleSystem *psys); void BKE_ptcache_id_from_cloth(PTCacheID *pid, struct Object *ob, struct ClothModifierData *clmd); void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct SmokeModifierData *smd); +void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, struct Object *ob, struct DynamicPaintSurface *surface); void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Object *ob, struct Scene *scene, int duplis); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index defcef58463..a869c23f868 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -95,6 +95,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/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index d9c98bc0200..ebf21941706 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -1669,7 +1669,7 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos Mesh *me = ob->data; ModifierData *firstmd, *md; LinkNode *datamasks, *curr; - CustomDataMask mask, nextmask; + CustomDataMask mask, nextmask, append_mask = 0; float (*deformedVerts)[3] = NULL; DerivedMesh *dm, *orcodm, *clothorcodm, *finaldm; int numVerts = me->totvert; @@ -1883,6 +1883,7 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos /* set the DerivedMesh to only copy needed data */ mask= (CustomDataMask)GET_INT_FROM_POINTER(curr->link); + mask |= append_mask; DM_set_only_copy(dm, mask); /* add cloth rest shape key if need */ @@ -1941,6 +1942,10 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos clothorcodm = ndm; } } + + /* in case of dynamic paint, make sure preview mask remains for following modifiers */ + if (md->type == eModifierType_DynamicPaint) + append_mask |= CD_MASK_WEIGHT_MCOL; } isPrevDeform= (mti->type == eModifierTypeType_OnlyDeform); diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c index c3aeb440938..91238f20337 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/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 3abfa05e1fd..a184088aa17 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -881,7 +881,9 @@ static void cdDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us if(useColors && mc) cp = (unsigned char *)&mc[i * 4]; - glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT); + /* dont set shading mode to flat because + * normals are used to change shading */ + glShadeModel(GL_SMOOTH); glBegin(mf->v4?GL_QUADS:GL_TRIANGLES); if (!drawSmooth) { diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c new file mode 100644 index 00000000000..f2b4cc7d351 --- /dev/null +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -0,0 +1,4871 @@ +/** +***** 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 <math.h> +#include <stdio.h> + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_kdtree.h" +#include "BLI_utildefines.h" + +/* Platform independend time */ +#include "PIL_time.h" + +#include "BKE_animsys.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_deform.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_dynamicpaint.h" +#include "BKE_effect.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_texture.h" + +#include "DNA_anim_types.h" +#include "DNA_dynamicpaint_types.h" +#include "DNA_group_types.h" /*GroupObject*/ +#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 */ + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +/* 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" + +/* to read object material color */ +#include "DNA_texture_types.h" +#include "../render/intern/include/pointdensity.h" +#include "../render/intern/include/render_types.h" +#include "../render/intern/include/voxeldata.h" +#include "DNA_material_types.h" +#include "RE_render_ext.h" + +#ifdef _OPENMP +#include <omp.h> +#endif + +#define DPOUTPUT_JPEG 0 +#define DPOUTPUT_PNG 1 +#define DPOUTPUT_OPENEXR 2 + +struct Object; +struct Scene; +struct DerivedMesh; + +/* precalculated gaussian factors for 5x super sampling */ +float gaussianFactors[5] = { 0.996849f, + 0.596145f, + 0.596145f, + 0.596145f, + 0.524141f}; +float gaussianTotal = 3.309425f; + +/* +* UV Image 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}; + +static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *surface, float timescale, float subframe); +static int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Scene *scene, Object *cObject, int frame); + +/***************************** Internal Structs ***************************/ + +typedef struct Bounds2D { + float min[2], max[2]; +} Bounds2D; + +typedef struct Bounds3D { + float min[3], max[3]; +} Bounds3D; + +typedef struct VolumeGrid { + int x,y,z; + Bounds3D grid_bounds; + + Bounds3D *bounds; /* (x*y*z) precalculated grid cell bounds */ + unsigned int *s_pos; /* (x*y*z) search indexses */ + unsigned int *s_num; /* (x*y*z) number of points */ + unsigned int *t_index; /* actual point index, + access: (s_pos+s_num) */ +} VolumeGrid; + +typedef struct Vec3f { + float v[3]; +} Vec3f; + +typedef struct BakeNeighPoint { + float dir[3]; /* vector pointing towards this neighbour */ + float dist; /* distance to */ +} BakeNeighPoint; + +/* Surface data used while processing a frame */ +typedef struct PaintBakeNormal { + float invNorm[3]; /* current pixel world-space inverted normal */ + float normal_scale; /* normal directional scale for displace mapping */ +} PaintBakeNormal; + +/* Temp surface data used to process a frame */ +typedef struct PaintBakeData { + PaintBakeNormal *bNormal; + unsigned int *s_pos; /* index to start reading point sample realCoord */ + unsigned int *s_num; /* num of samples for each point */ + Vec3f *realCoord; /* current pixel center world-space coordinates * numOfSamples + * ordered as (s_pos+sample_num)*/ + + BakeNeighPoint *bNeighs; /* current frame neighbour distances, if required */ + VolumeGrid *grid; /* space partitioning grid to optimize brush checks */ + + MVert *prev_verts; /* copy of previous frame vertices. used observe surface movement */ + float prev_obmat[4][4]; /* previous frame object matrix */ + +} PaintBakeData; + +/* UV Image sequence format point */ +typedef struct PaintUVPoint { + /* Pixel / mesh data */ + unsigned int face_index, pixel_index; /* face index on domain derived mesh */ + unsigned int v1, v2, v3; /* vertex indexes */ + + unsigned int neighbour_pixel; /* If this pixel isn't uv mapped to any face, + but it's neighbouring pixel is */ + short quad; +} PaintUVPoint; + +typedef struct ImgSeqFormatData { + PaintUVPoint *uv_p; + Vec3f *barycentricWeights; /* b-weights for all pixel samples */ +} ImgSeqFormatData; + +typedef struct EffVelPoint { + float previous_pos[3]; + float previous_vel[3]; +} EffVelPoint; + + +/* adjacency data flags */ +#define ADJ_ON_MESH_EDGE (1<<0) + +typedef struct PaintAdjData { + unsigned int *n_target; /* array of neighbouring point indexes, + for single sample use (n_index+neigh_num) */ + unsigned int *n_index; /* index to start reading n_target for each point */ + unsigned int *n_num; /* num of neighs for each point */ + unsigned int *flags; /* vertex adjacency flags */ + unsigned int total_targets; /* size of n_target */ +} PaintAdjData; + +/***************************** General Utils ******************************/ + +/* +* Output error message to both ui and console +*/ +static int printError(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); + + return 0; +} + +/* Get number of surface points for cached types */ +static int dynamicPaint_surfaceNumOfPoints(DynamicPaintSurface *surface) +{ + if (surface->format == MOD_DPAINT_SURFACE_F_PTEX) { + return 0; /* not supported atm */ + } + else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + if (!surface->canvas->dm) return 0; /* invalid derived mesh */ + return surface->canvas->dm->getNumVerts(surface->canvas->dm); + } + else + return 0; +} + +/* checks whether surface's format/type has realtime preview */ +int dynamicPaint_surfaceHasColorPreview(DynamicPaintSurface *surface) { + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) return 0; + else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WAVE) return 0; + else return 1; + } + else return 1; +} + +/* get currently active surface (in user interface) */ +static DynamicPaintSurface *get_activeSurface(DynamicPaintCanvasSettings *canvas) +{ + DynamicPaintSurface *surface = canvas->surfaces.first; + int i; + + for(i=0; surface; surface=surface->next) { + if(i == canvas->active_sur) + return surface; + i++; + } + return NULL; +} + +/* set preview to first previewable surface */ +static void dynamicPaint_resetPreview(DynamicPaintCanvasSettings *canvas) +{ + DynamicPaintSurface *surface = canvas->surfaces.first; + int done=0; + + for(; surface; surface=surface->next) { + if (!done && dynamicPaint_surfaceHasColorPreview(surface)) { + surface->flags |= MOD_DPAINT_PREVIEW; + done=1; + } + else + surface->flags &= ~MOD_DPAINT_PREVIEW; + } +} + +/* set preview to defined surface */ +static void dynamicPaint_setPreview(DynamicPaintSurface *t_surface) +{ + DynamicPaintSurface *surface = t_surface->canvas->surfaces.first; + for(; surface; surface=surface->next) { + if (surface == t_surface) + surface->flags |= MOD_DPAINT_PREVIEW; + else + surface->flags &= ~MOD_DPAINT_PREVIEW; + } +} + +/* change surface data to defaults on new type */ +void dynamicPaintSurface_updateType(struct DynamicPaintSurface *surface) { + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { + surface->output_name[0]='\0'; + surface->output_name2[0]='\0'; + surface->flags |= MOD_DPAINT_ANTIALIAS; + } + else { + sprintf(surface->output_name, "dp_"); + strcpy(surface->output_name2,surface->output_name); + surface->flags &= ~MOD_DPAINT_ANTIALIAS; + } + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + strcat(surface->output_name,"paintmap"); + strcat(surface->output_name2,"wetmap"); + } + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { + strcat(surface->output_name,"displace"); + } + else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { + strcat(surface->output_name,"weight"); + } + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + strcat(surface->output_name,"wave"); + strcat(surface->output_name2,"foam"); + } + + /* update preview */ + if (dynamicPaint_surfaceHasColorPreview(surface)) + dynamicPaint_setPreview(surface); + else + dynamicPaint_resetPreview(surface->canvas); +} + +static int surfaceDublicateNameExists(void *arg, const char *name) +{ + DynamicPaintSurface *t_surface = (DynamicPaintSurface*)arg; + DynamicPaintSurface *surface = t_surface->canvas->surfaces.first; + + for(; surface; surface=surface->next) { + if (surface!=t_surface && !strcmp(name, surface->name)) return 1; + } + return 0; +} + +void dynamicPaintSurface_setUniqueName(DynamicPaintSurface *surface, char *basename) { + char name[64]; + strncpy(name, basename, 62); /* in case basename is surface->name use a copy */ + BLI_uniquename_cb(surfaceDublicateNameExists, surface, name, '.', surface->name, sizeof(surface->name)); +} + +static int surface_totalSamples(DynamicPaintSurface *surface) +{ + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ && + surface->flags & MOD_DPAINT_ANTIALIAS) + return (surface->data->total_points*5); + if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && + surface->flags & MOD_DPAINT_ANTIALIAS && surface->data->adj_data) + return (surface->data->total_points+surface->data->adj_data->total_targets); + + return surface->data->total_points; +} + +/* assumes source alpha > 0.0f or results NaN colors */ +static void mixColors(float *t_color, float t_alpha, float *s_color, float s_alpha) +{ + float invFact = (s_alpha<t_alpha) ? 1.0f : t_alpha/s_alpha; + float factor = 1.0f - invFact; + + /* set initial color depending on existing alpha */ + t_color[0] = t_color[0]*invFact + s_color[0]*factor; + t_color[1] = t_color[1]*invFact + s_color[1]*factor; + t_color[2] = t_color[2]*invFact + s_color[2]*factor; + + /* mix final color */ + factor = s_alpha; + invFact = 1.0f - factor; + t_color[0] = t_color[0]*invFact + s_color[0]*factor; + t_color[1] = t_color[1]*invFact + s_color[1]*factor; + t_color[2] = t_color[2]*invFact + s_color[2]*factor; +} + +static int boundsIntersect(Bounds3D *b1, Bounds3D *b2) +{ + int i=2; + for (; i>=0; i-=1) + if (!(b1->min[i] <= b2->max[i] && b1->max[i] >= b2->min[i])) return 0; + return 1; +} + +static int boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, float dist) +{ + int i=2; + for (; i>=0; i-=1) + if (!(b1->min[i] <= (b2->max[i]+dist) && b1->max[i] >= (b2->min[i]-dist))) return 0; + return 1; +} + +static int boundIntersectPoint(Bounds3D *b, float point[3], float radius) +{ + int i=2; + for (; i>=0; i-=1) + if (!(b->min[i] <= (point[i]+radius) && b->max[i] >= (point[i]-radius))) return 0; + return 1; +} + +static void boundInsert(Bounds3D *b, float point[3]) +{ + int i=2; + for (; i>=0; i-=1) { + if (point[i] < b->min[i]) b->min[i]=point[i]; + if (point[i] > b->max[i]) b->max[i]=point[i]; + } +} + +static void freeGrid(PaintSurfaceData *data) +{ + PaintBakeData *bData = data->bData; + VolumeGrid *grid = bData->grid; + + if (grid->bounds) MEM_freeN(grid->bounds); + if (grid->s_pos) MEM_freeN(grid->s_pos); + if (grid->s_num) MEM_freeN(grid->s_num); + if (grid->t_index) MEM_freeN(grid->t_index); + + MEM_freeN(bData->grid); + bData->grid = NULL; +} + +static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) +{ + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + Bounds3D *grid_bounds; + VolumeGrid *grid; + int grid_cells, axis = 3; + int *temp_t_index = NULL; + int *temp_s_num = NULL; + +#ifdef _OPENMP + int num_of_threads = omp_get_max_threads(); +#else + int num_of_threads = 1; +#endif + + if (bData->grid) + freeGrid(sData); + + /* allocate separate bounds for each thread */ + grid_bounds = MEM_callocN(sizeof(Bounds3D)*num_of_threads, "Grid Bounds"); + + bData->grid = MEM_callocN(sizeof(VolumeGrid), "Surface Grid"); + grid = bData->grid; + + if (grid && grid_bounds) { + int index, error = 0; + float dim_factor, volume, dim[3]; + float tx,ty,tz; + + /* initial values for each thread */ + for (index = 0; index<num_of_threads; index++) { + VECCOPY(grid_bounds[index].min, bData->realCoord[bData->s_pos[0]].v); + VECCOPY(grid_bounds[index].max, bData->realCoord[bData->s_pos[0]].v); + } + VECCOPY(grid->grid_bounds.min, bData->realCoord[bData->s_pos[0]].v); + VECCOPY(grid->grid_bounds.max, bData->realCoord[bData->s_pos[0]].v); + + /* calculate canvas dimensions */ + #pragma omp parallel for schedule(static) + for (index = 1; index < sData->total_points; index++) { +#ifdef _OPENMP + int id = omp_get_thread_num(); + boundInsert(&grid_bounds[id], (bData->realCoord[bData->s_pos[index]].v)); +#else + boundInsert(&grid_bounds[0], (bData->realCoord[bData->s_pos[index]].v)); +#endif + } + + /* get final dimensions */ + for (index = 0; index<num_of_threads; index++) { + boundInsert(&grid->grid_bounds, grid_bounds[index].min); + boundInsert(&grid->grid_bounds, grid_bounds[index].max); + } + + dim[0] = grid->grid_bounds.max[0]-grid->grid_bounds.min[0]; + dim[1] = grid->grid_bounds.max[1]-grid->grid_bounds.min[1]; + dim[2] = grid->grid_bounds.max[2]-grid->grid_bounds.min[2]; + + tx = dim[0]; + ty = dim[1]; + tz = dim[2]; + + /* deactivate zero axises */ + if (!tx) {tx=1.0f; axis-=1;} + if (!ty) {ty=1.0f; axis-=1;} + if (!tz) {tz=1.0f; axis-=1;} + + if (axis == 0) + return; + + /* now with values scaled >= 1.0f, calculate scaled grid volume */ + volume = tx*ty*tz; + + /* determine final grid size by trying to fit average 10.000 points per grid cell */ + dim_factor = pow(volume / ((double)sData->total_points / 10000.f), 1.0f/axis); + + /* define final grid size using dim_factor, use min 3 for active axises */ + grid->x = (int)floor(tx / dim_factor); + CLAMP(grid->x, (dim[0]) ? 3 : 1, 100); + grid->y = (int)floor(ty / dim_factor); + CLAMP(grid->y, (dim[1]) ? 3 : 1, 100); + grid->z = (int)floor(tz / dim_factor); + CLAMP(grid->z, (dim[2]) ? 3 : 1, 100); + + grid_cells = grid->x*grid->y*grid->z; + + //printf("final grid size %i,%i,%i\n", grid->x, grid->y, grid->z); + + /* allocate memory for grids */ + + grid->bounds = MEM_callocN(sizeof(Bounds3D) * grid_cells, "Surface Grid Bounds"); + grid->s_pos = MEM_callocN(sizeof(int) * grid_cells, "Surface Grid Position"); + grid->s_num = MEM_callocN(sizeof(int) * grid_cells*num_of_threads, "Surface Grid Points"); + temp_s_num = MEM_callocN(sizeof(int) * grid_cells, "Temp Surface Grid Points"); + grid->t_index = MEM_callocN(sizeof(int) * sData->total_points, "Surface Grid Target Ids"); + temp_t_index = MEM_callocN(sizeof(int) * sData->total_points, "Temp Surface Grid Target Ids"); + + if (!grid->bounds || !grid->s_pos || !grid->s_num || !grid->t_index || !temp_s_num || !temp_t_index) + error = 1; + + if (!error) { + /* calculate number of points withing each cell */ + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) { + int x,y,z; + x = floor((bData->realCoord[bData->s_pos[index]].v[0] - grid->grid_bounds.min[0])/dim[0]*grid->x); + CLAMP(x, 0, grid->x-1); + y = floor((bData->realCoord[bData->s_pos[index]].v[1] - grid->grid_bounds.min[1])/dim[1]*grid->y); + CLAMP(y, 0, grid->y-1); + z = floor((bData->realCoord[bData->s_pos[index]].v[2] - grid->grid_bounds.min[2])/dim[2]*grid->z); + CLAMP(z, 0, grid->z-1); + + temp_t_index[index] = x + y * grid->x + z * grid->x*grid->y; +#ifdef _OPENMP + grid->s_num[temp_t_index[index]+omp_get_thread_num()*grid_cells]++; +#else + grid->s_num[temp_t_index[index]]++; +#endif + } + + /* for first cell only calc s_num */ + for (index = 1; index<num_of_threads; index++) { + grid->s_num[0] += grid->s_num[index*grid_cells]; + } + + /* calculate grid indexes */ + for (index = 1; index < grid_cells; index++) { + int id; + for (id = 1; id<num_of_threads; id++) { + grid->s_num[index] += grid->s_num[index+id*grid_cells]; + } + grid->s_pos[index] = grid->s_pos[index-1] + grid->s_num[index-1]; + } + + /* save point indexes to final array */ + for (index = 0; index < sData->total_points; index++) { + int pos = grid->s_pos[temp_t_index[index]] + temp_s_num[temp_t_index[index]]; + grid->t_index[pos] = index; + + temp_s_num[temp_t_index[index]]++; + } + + /* calculate cell bounds */ + { + int x; + #pragma omp parallel for schedule(static) + for (x=0; x<grid->x; x++) { + int y; + for (y=0; y<grid->y; y++) { + int z; + for (z=0; z<grid->z; z++) { + index = x + y * grid->x + z * grid->x*grid->y; + + /* set bounds */ + grid->bounds[index].min[0] = grid->grid_bounds.min[0] + dim[0]/grid->x*x; + grid->bounds[index].min[1] = grid->grid_bounds.min[1] + dim[1]/grid->y*y; + grid->bounds[index].min[2] = grid->grid_bounds.min[2] + dim[2]/grid->z*z; + + grid->bounds[index].max[0] = grid->grid_bounds.min[0] + dim[0]/grid->x*(x+1); + grid->bounds[index].max[1] = grid->grid_bounds.min[1] + dim[1]/grid->y*(y+1); + grid->bounds[index].max[2] = grid->grid_bounds.min[2] + dim[2]/grid->z*(z+1); + } + } + } + } + } + + if (temp_s_num) MEM_freeN(temp_s_num); + if (temp_t_index) MEM_freeN(temp_t_index); + + /* free per thread s_num values */ + grid->s_num = MEM_reallocN(grid->s_num, sizeof(int) * grid_cells); + + if (error || !grid->s_num) + freeGrid(sData); + } + + if (grid_bounds) MEM_freeN(grid_bounds); +} + +/***************************** Freeing data ******************************/ + +/* Free brush data */ +static void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd) +{ + if(pmd->brush) { + if(pmd->brush->dm) + pmd->brush->dm->release(pmd->brush->dm); + pmd->brush->dm = NULL; + + if(pmd->brush->paint_ramp) + MEM_freeN(pmd->brush->paint_ramp); + pmd->brush->paint_ramp = NULL; + + MEM_freeN(pmd->brush); + pmd->brush = NULL; + } +} + +static void dynamicPaint_freeAdjData(PaintSurfaceData *data) +{ + if (data->adj_data) { + if (data->adj_data->n_index) MEM_freeN(data->adj_data->n_index); + if (data->adj_data->n_num) MEM_freeN(data->adj_data->n_num); + if (data->adj_data->n_target) MEM_freeN(data->adj_data->n_target); + if (data->adj_data->flags) MEM_freeN(data->adj_data->flags); + MEM_freeN(data->adj_data); + data->adj_data = NULL; + } +} + +static void free_bakeData(PaintSurfaceData *data) +{ + PaintBakeData *bData = data->bData; + if (bData) { + if (bData->bNormal) MEM_freeN(bData->bNormal); + if (bData->s_pos) MEM_freeN(bData->s_pos); + if (bData->s_num) MEM_freeN(bData->s_num); + if (bData->realCoord) MEM_freeN(bData->realCoord); + if (bData->bNeighs) MEM_freeN(bData->bNeighs); + if (bData->grid) freeGrid(data); + if (bData->prev_verts) MEM_freeN(bData->prev_verts); + + MEM_freeN(data->bData); + data->bData = NULL; + } +} + +static void dynamicPaint_freeSurfaceData(DynamicPaintSurface *surface) +{ + PaintSurfaceData *data = surface->data; + if (!data) return; + if (data->format_data) { + /* format specific free */ + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { + ImgSeqFormatData *format_data = (ImgSeqFormatData*)data->format_data; + if (format_data->uv_p) + MEM_freeN(format_data->uv_p); + if (format_data->barycentricWeights) + MEM_freeN(format_data->barycentricWeights); + } + MEM_freeN(data->format_data); + } + /* type data */ + if (data->type_data) MEM_freeN(data->type_data); + dynamicPaint_freeAdjData(data); + /* bake data */ + free_bakeData(data); + + MEM_freeN(surface->data); + surface->data = NULL; +} + +static void dynamicPaint_freeSurface(DynamicPaintSurface *surface) +{ + if (!surface) return; + + /* point cache */ + BKE_ptcache_free_list(&(surface->ptcaches)); + surface->pointcache = NULL; + + if(surface->effector_weights) + MEM_freeN(surface->effector_weights); + surface->effector_weights = NULL; + + BLI_remlink(&(surface->canvas->surfaces), surface); + dynamicPaint_freeSurfaceData(surface); + MEM_freeN(surface); +} + +/* Free canvas data */ +static void dynamicPaint_freeCanvas(DynamicPaintModifierData *pmd) +{ + if(pmd->canvas) { + /* Free surface data */ + DynamicPaintSurface *surface = pmd->canvas->surfaces.first; + DynamicPaintSurface *next_surface = NULL; + + while (surface) { + next_surface = surface->next; + dynamicPaint_freeSurface(surface); + surface = next_surface; + } + + /* free dm copy */ + if (pmd->canvas->dm) + pmd->canvas->dm->release(pmd->canvas->dm); + pmd->canvas->dm = NULL; + + MEM_freeN(pmd->canvas); + pmd->canvas = NULL; + } +} + +/* Free whole dp modifier */ +void dynamicPaint_Modifier_free(struct DynamicPaintModifierData *pmd) +{ + if(pmd) { + dynamicPaint_freeCanvas(pmd); + dynamicPaint_freeBrush(pmd); + } +} + + +/***************************** Initialize and reset ******************************/ + +/* +* Creates a new surface and adds it to the list +* A pointer to this surface is returned +*/ +static DynamicPaintSurface *dynamicPaint_createNewSurface(DynamicPaintCanvasSettings *canvas, Scene *scene) +{ + DynamicPaintSurface *surface= MEM_callocN(sizeof(DynamicPaintSurface), "DynamicPaintSurface"); + if (!surface) return NULL; + + surface->canvas = canvas; + surface->format = MOD_DPAINT_SURFACE_F_VERTEX; + surface->type = MOD_DPAINT_SURFACE_T_PAINT; + + /* cache */ + surface->pointcache = BKE_ptcache_add(&(surface->ptcaches)); + surface->pointcache->flag |= PTCACHE_DISK_CACHE; + surface->pointcache->step = 1; + + /* Set initial values */ + surface->flags = MOD_DPAINT_ANTIALIAS | MOD_DPAINT_MULALPHA | MOD_DPAINT_DRY_LOG | MOD_DPAINT_DISSOLVE_LOG | + MOD_DPAINT_ACTIVE | MOD_DPAINT_PREVIEW | MOD_DPAINT_OUT1; + surface->effect = 0; + surface->effect_ui = 1; + + surface->diss_speed = 300; + surface->dry_speed = 300; + surface->disp_depth = 1.0f; + surface->disp_type = MOD_DPAINT_DISP_DISPLACE; + surface->image_fileformat = MOD_DPAINT_IMGFORMAT_PNG; + + surface->image_resolution = 256; + surface->substeps = 0; + + if (scene) { + surface->start_frame = scene->r.sfra; + surface->end_frame = scene->r.efra; + } + else { + surface->start_frame = 1; + surface->end_frame = 250; + } + + surface->spread_speed = 1.0f; + surface->shrink_speed = 1.0f; + + surface->wave_damping = 0.05f; + surface->wave_speed = 0.8f; + surface->wave_timescale = 1.0f; + surface->wave_spring = 0.20; + + sprintf(surface->image_output_path, "%sdynamicpaint/", "/tmp/"); + dynamicPaintSurface_setUniqueName(surface, "Surface"); + + surface->effector_weights = BKE_add_effector_weights(NULL); + + dynamicPaintSurface_updateType(surface); + + BLI_addtail(&canvas->surfaces, surface); + + return surface; +} + +/* +* Initialize modifier data +*/ +int dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, struct Scene *scene) +{ + if(pmd) + { + if(type == MOD_DYNAMICPAINT_TYPE_CANVAS) + { + if(pmd->canvas) + dynamicPaint_freeCanvas(pmd); + + pmd->canvas = MEM_callocN(sizeof(DynamicPaintCanvasSettings), "DynamicPaint Canvas"); + if (!pmd->canvas) + return 0; + pmd->canvas->pmd = pmd; + pmd->canvas->dm = NULL; + + /* Create one surface */ + if (!dynamicPaint_createNewSurface(pmd->canvas, scene)) + return 0; + + pmd->canvas->ui_info[0] = '\0'; + + } + else if(type == MOD_DYNAMICPAINT_TYPE_BRUSH) + { + if(pmd->brush) + dynamicPaint_freeBrush(pmd); + + pmd->brush = MEM_callocN(sizeof(DynamicPaintBrushSettings), "DynamicPaint Paint"); + if (!pmd->brush) + return 0; + pmd->brush->pmd = pmd; + + pmd->brush->psys = NULL; + + pmd->brush->flags = MOD_DPAINT_ABS_ALPHA; + pmd->brush->collision = MOD_DPAINT_COL_VOLUME; + + pmd->brush->mat = NULL; + pmd->brush->r = 1.0f; + pmd->brush->g = 1.0f; + pmd->brush->b = 1.0f; + pmd->brush->alpha = 1.0f; + pmd->brush->wetness = 1.0f; + + pmd->brush->paint_distance = 0.1f; + pmd->brush->proximity_falloff = MOD_DPAINT_PRFALL_SMOOTH; + + pmd->brush->displace_distance = 0.5f; + pmd->brush->prox_displace_strength = 0.5f; + + pmd->brush->particle_radius = 0.2f; + pmd->brush->particle_smooth = 0.05f; + + pmd->brush->wave_factor = 1.0f; + + pmd->brush->dm = NULL; + + /* + * Paint proximity falloff colorramp. + */ + { + CBData *ramp; + + pmd->brush->paint_ramp = add_colorband(0); + if (!pmd->brush->paint_ramp) + return 0; + ramp = pmd->brush->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->brush->paint_ramp->tot = 2; + } + } + } + else + return 0; + + return 1; +} + +void dynamicPaint_Modifier_copy(struct DynamicPaintModifierData *pmd, struct DynamicPaintModifierData *tpmd) +{ + /* Init modifier */ + tpmd->type = pmd->type; + if (pmd->canvas) + dynamicPaint_createType(tpmd, MOD_DYNAMICPAINT_TYPE_CANVAS, NULL); + if (pmd->brush) + dynamicPaint_createType(tpmd, MOD_DYNAMICPAINT_TYPE_BRUSH, NULL); + + /* Copy data */ + if (tpmd->canvas) { + pmd->canvas->pmd = tpmd; + + tpmd->canvas->ui_info[0] = '\0'; + + } else if (tpmd->brush) { + pmd->brush->pmd = tpmd; + + tpmd->brush->flags = pmd->brush->flags; + tpmd->brush->collision = pmd->brush->collision; + + tpmd->brush->r = pmd->brush->r; + tpmd->brush->g = pmd->brush->g; + tpmd->brush->b = pmd->brush->b; + tpmd->brush->alpha = pmd->brush->alpha; + tpmd->brush->wetness = pmd->brush->wetness; + + tpmd->brush->particle_radius = pmd->brush->particle_radius; + tpmd->brush->particle_smooth = pmd->brush->particle_smooth; + tpmd->brush->paint_distance = pmd->brush->paint_distance; + tpmd->brush->psys = pmd->brush->psys; + tpmd->brush->displace_distance = pmd->brush->displace_distance; + tpmd->brush->prox_displace_strength = pmd->brush->prox_displace_strength; + + tpmd->brush->paint_ramp = pmd->brush->paint_ramp; + + tpmd->brush->proximity_falloff = pmd->brush->proximity_falloff; + } +} + +/* allocates surface data depending on surface type */ +static void dynamicPaint_allocateSurfaceType(DynamicPaintSurface *surface) +{ + PaintSurfaceData *sData = surface->data; + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + sData->type_data = MEM_callocN(sizeof(PaintPoint)*sData->total_points, "DynamicPaintSurface Data"); + } + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { + sData->type_data = MEM_callocN(sizeof(float)*sData->total_points, "DynamicPaintSurface DepthData"); + } + else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { + sData->type_data = MEM_callocN(sizeof(float)*sData->total_points, "DynamicPaintSurface WeightData"); + } + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + sData->type_data = MEM_callocN(sizeof(PaintWavePoint)*sData->total_points, "DynamicPaintSurface WaveData"); + } + else return; + + if (sData->type_data == NULL) printError(surface->canvas, "Not enough free memory!"); +} + +static int surface_usesAdjDistance(DynamicPaintSurface *surface) { + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && surface->effect) return 1; + if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) return 1; + return 0; +} + +static int surface_usesAdjData(DynamicPaintSurface *surface) { + if (surface_usesAdjDistance(surface)) return 1; + if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && + surface->flags & MOD_DPAINT_ANTIALIAS) return 1; + + return 0; +} + +/* initialize surface adjacency data */ +static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface) { + PaintSurfaceData *sData = surface->data; + PaintAdjData *ed; + int *temp_data; + int neigh_points = 0; + + if (!surface_usesAdjData(surface)) return; + + if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + /* For vertex format, neighbours are connected by edges */ + neigh_points = 2*surface->canvas->dm->getNumEdges(surface->canvas->dm); + } + else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + neigh_points = sData->total_points*8; + + if (!neigh_points) return; + + /* allocate memory */ + ed = sData->adj_data = MEM_callocN(sizeof(PaintAdjData), "Surface Adj Data"); + if (!ed) return; + ed->n_index = MEM_callocN(sizeof(int)*sData->total_points, "Surface Adj Index"); + ed->n_num = MEM_callocN(sizeof(int)*sData->total_points, "Surface Adj Counts"); + temp_data = MEM_callocN(sizeof(int)*sData->total_points, "Temp Adj Data"); + ed->n_target = MEM_callocN(sizeof(int)*neigh_points, "Surface Adj Targets"); + ed->flags = MEM_callocN(sizeof(int)*sData->total_points, "Surface Adj Flags"); + ed->total_targets = neigh_points; + + /* in case of error, free allocated memory */ + if (!ed->n_index || !ed->n_num || !ed->n_target || !temp_data) { + dynamicPaint_freeAdjData(sData); + MEM_freeN(temp_data); + printError(surface->canvas, "Not enough free memory."); + return; + } + + if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + int i; + int n_pos; + + /* For vertex format, count every vertex that is connected by an edge */ + int numOfEdges = surface->canvas->dm->getNumEdges(surface->canvas->dm); + int numOfFaces = surface->canvas->dm->getNumFaces(surface->canvas->dm); + struct MEdge *edge = surface->canvas->dm->getEdgeArray(surface->canvas->dm); + struct MFace *face = surface->canvas->dm->getFaceArray(surface->canvas->dm); + + /* count number of edges per vertex */ + for (i=0; i<numOfEdges; i++) { + ed->n_num[edge[i].v1]++; + ed->n_num[edge[i].v2]++; + + temp_data[edge[i].v1]++; + temp_data[edge[i].v2]++; + } + + /* to locate points on "mesh edge" */ + for (i=0; i<numOfFaces; i++) { + temp_data[face[i].v1]++; + temp_data[face[i].v2]++; + temp_data[face[i].v3]++; + if (face[i].v4) + temp_data[face[i].v4]++; + } + + /* now check if total number of edges+faces for + * each vertex is even, if not -> vertex is on mesh edge */ + for (i=0; i<sData->total_points; i++) { + if ((temp_data[i]%2) || + temp_data[i] < 4) + ed->flags[i] |= ADJ_ON_MESH_EDGE; + + /* reset temp data */ + temp_data[i] = 0; + } + + /* order n_index array */ + n_pos = 0; + for (i=0; i<sData->total_points; i++) { + ed->n_index[i] = n_pos; + n_pos += ed->n_num[i]; + } + + /* and now add neighbour data using that info */ + for (i=0; i<numOfEdges; i++) { + /* first vertex */ + int index = edge[i].v1; + n_pos = ed->n_index[index]+temp_data[index]; + ed->n_target[n_pos] = edge[i].v2; + temp_data[index]++; + + /* second vertex */ + index = edge[i].v2; + n_pos = ed->n_index[index]+temp_data[index]; + ed->n_target[n_pos] = edge[i].v1; + temp_data[index]++; + } + } + else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { + /* for image sequences, only allocate memory. + * bake initialization takes care of rest */ + } + + MEM_freeN(temp_data); +} + +/* clears surface data back to zero */ +void dynamicPaint_clearSurface(DynamicPaintSurface *surface) { + PaintSurfaceData *sData = surface->data; + if (sData && sData->type_data) { + unsigned int data_size; + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) + data_size = sizeof(PaintPoint); + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) + data_size = sizeof(PaintWavePoint); + else + data_size = sizeof(float); + + memset(sData->type_data, 0, data_size * sData->total_points); + } +} + +/* completely (re)initializes surface (only for point cache types)*/ +int dynamicPaint_resetSurface(DynamicPaintSurface *surface) +{ + int numOfPoints = dynamicPaint_surfaceNumOfPoints(surface); + /* dont touch image sequence types. they get handled only on bake */ + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) return 1; + + if (surface->data) dynamicPaint_freeSurfaceData(surface); + if (numOfPoints < 1) return 0; + + /* allocate memory */ + surface->data = MEM_callocN(sizeof(PaintSurfaceData), "PaintSurfaceData"); + if (!surface->data) return 0; + + /* allocate data depending on surface type and format */ + surface->data->total_points = numOfPoints; + dynamicPaint_allocateSurfaceType(surface); + dynamicPaint_initAdjacencyData(surface); + + return 1; +} + +/* make sure allocated surface size matches current requirements */ +static void dynamicPaint_checkSurfaceData(DynamicPaintSurface *surface) +{ + if (!surface->data || ((dynamicPaint_surfaceNumOfPoints(surface) != surface->data->total_points))) { + dynamicPaint_resetSurface(surface); + } +} + + +/***************************** Modifier processing ******************************/ + + +/* update cache frame range */ +void dynamicPaint_cacheUpdateFrames(DynamicPaintSurface *surface) { + if (surface->pointcache) { + surface->pointcache->startframe = surface->start_frame; + surface->pointcache->endframe = surface->end_frame; + } +} + +/* +* Updates derived mesh copy and processes dynamic paint step / caches. +*/ +static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm) +{ + if(pmd->canvas) { + DynamicPaintCanvasSettings *canvas = pmd->canvas; + DynamicPaintSurface *surface = canvas->surfaces.first; + + /* update derived mesh copy */ + if (canvas->dm) canvas->dm->release(canvas->dm); + canvas->dm = CDDM_copy(dm); + + /* in case image sequence baking, stop here */ + if (canvas->flags & MOD_DPAINT_BAKING) return; + + /* loop through surfaces */ + for (; surface; surface=surface->next) { + int current_frame = (int)scene->r.cfra; + + /* image sequences are handled by bake operator */ + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) continue; + if (!(surface->flags & MOD_DPAINT_ACTIVE)) continue; + + /* make sure surface is valid */ + dynamicPaint_checkSurfaceData(surface); + + /* limit frame range */ + CLAMP(current_frame, surface->start_frame, surface->end_frame); + + if (current_frame != surface->current_frame || (int)scene->r.cfra == surface->start_frame) { + PointCache *cache = surface->pointcache; + PTCacheID pid; + surface->current_frame = current_frame; + + /* read point cache */ + BKE_ptcache_id_from_dynamicpaint(&pid, ob, surface); + pid.cache->startframe = surface->start_frame; + pid.cache->endframe = surface->end_frame; + BKE_ptcache_id_time(&pid, scene, scene->r.cfra, NULL, NULL, NULL); + + /* reset non-baked cache at first frame */ + if((int)scene->r.cfra == surface->start_frame && !(cache->flag & PTCACHE_BAKED)) + { + cache->flag |= PTCACHE_REDO_NEEDED; + BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); + cache->flag &= ~PTCACHE_REDO_NEEDED; + } + + /* try to read from cache */ + if(BKE_ptcache_read(&pid, (float)scene->r.cfra)) { + BKE_ptcache_validate(cache, (int)scene->r.cfra); + } + /* if read failed and we're on surface range do recalculate */ + else if ((int)scene->r.cfra == current_frame) { + /* calculate surface frame */ + canvas->flags |= MOD_DPAINT_BAKING; + dynamicPaint_calculateFrame(surface, scene, ob, current_frame); + canvas->flags &= ~MOD_DPAINT_BAKING; + + BKE_ptcache_validate(cache, surface->current_frame); + BKE_ptcache_write(&pid, surface->current_frame); + } + } + } + } +} + +/* +* Apply canvas data to the object derived mesh +*/ +struct DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm) +{ + DerivedMesh *result = CDDM_copy(dm); + + if(pmd->canvas && !(pmd->canvas->flags & MOD_DPAINT_BAKING)) { + + DynamicPaintSurface *surface = pmd->canvas->surfaces.first; + pmd->canvas->flags &= ~MOD_DPAINT_PREVIEW_READY; + + /* loop through surfaces */ + for (; surface; surface=surface->next) { + + if (surface && surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->data) { + if (!(surface->flags & (MOD_DPAINT_ACTIVE))) continue; + + /* process vertex surface previews */ + if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + + /* vertex color paint */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + + MFace *mface = result->getFaceArray(result); + int numOfFaces = result->getNumFaces(result); + int i; + PaintPoint* pPoint = (PaintPoint*)surface->data->type_data; + MCol *col; + + /* paint is stored on dry and wet layers, so mix final color first */ + float *fcolor = MEM_callocN(sizeof(float)*surface->data->total_points*4, "Temp paint color"); + + #pragma omp parallel for schedule(static) + for (i=0; i<surface->data->total_points; i++) { + int j=i*4; + + fcolor[j] = pPoint[i].color[0]; + fcolor[j+1] = pPoint[i].color[1]; + fcolor[j+2] = pPoint[i].color[2]; + /* mix colors */ + if (pPoint[i].e_alpha) mixColors(&fcolor[j], pPoint[i].alpha, pPoint[i].e_color, pPoint[i].e_alpha); + + /* Use highest alpha */ + fcolor[j+3] = (pPoint[i].e_alpha > pPoint[i].alpha) ? pPoint[i].e_alpha : pPoint[i].alpha; + } + + /* viewport preview */ + if (surface->flags & MOD_DPAINT_PREVIEW) { + /* Save preview results to weight layer, to be + * able to share same drawing methods */ + col = result->getFaceDataArray(result, CD_WEIGHT_MCOL); + if (!col) col = CustomData_add_layer(&result->faceData, CD_WEIGHT_MCOL, CD_CALLOC, NULL, numOfFaces); + + if (col) { + #pragma omp parallel for schedule(static) + for (i=0; i<numOfFaces; i++) { + int j=0; + float invAlpha; + Material *material = give_current_material(ob, mface[i].mat_nr+1); + + for (; j<((mface[i].v4)?4:3); j++) { + int index = (j==0)?mface[i].v1: (j==1)?mface[i].v2: (j==2)?mface[i].v3: mface[i].v4; + index *= 4; + invAlpha = 1.0f - fcolor[index+3]; + + /* Apply material color as base vertex color for preview */ + col[i*4+j].a = 255; + if (material) { + col[i*4+j].r = (unsigned char)(material->b*255); + col[i*4+j].g = (unsigned char)(material->g*255); + col[i*4+j].b = (unsigned char)(material->r*255); + } + else { + col[i*4+j].r = 165; + col[i*4+j].g = 165; + col[i*4+j].b = 165; + } + + /* mix surface color */ + col[i*4+j].r = (char)(((float)col[i*4+j].r)*invAlpha + (fcolor[index+2]*255*fcolor[index+3])); + col[i*4+j].g = (char)(((float)col[i*4+j].g)*invAlpha + (fcolor[index+1]*255*fcolor[index+3])); + col[i*4+j].b = (char)(((float)col[i*4+j].b)*invAlpha + (fcolor[index]*255*fcolor[index+3])); + } + } + pmd->canvas->flags |= MOD_DPAINT_PREVIEW_READY; + } + } + + + /* save layer data to output layer */ + + /* paint layer */ + col = CustomData_get_layer_named(&dm->faceData, CD_MCOL, surface->output_name); + if (col) { + #pragma omp parallel for schedule(static) + for (i=0; i<numOfFaces; i++) { + int j=0; + for (; j<((mface[i].v4)?4:3); j++) { + int index = (j==0)?mface[i].v1: (j==1)?mface[i].v2: (j==2)?mface[i].v3: mface[i].v4; + index *= 4; + + col[i*4+j].a = (char)(fcolor[index+3]*255); + col[i*4+j].r = (char)(fcolor[index+2]*255); + col[i*4+j].g = (char)(fcolor[index+1]*255); + col[i*4+j].b = (char)(fcolor[index]*255); + } + } + } + MEM_freeN(fcolor); + + /* wet layer */ + col = CustomData_get_layer_named(&dm->faceData, CD_MCOL, surface->output_name2); + if (col) { + #pragma omp parallel for schedule(static) + for (i=0; i<numOfFaces; i++) { + int j=0; + + for (; j<((mface[i].v4)?4:3); j++) { + int index = (j==0)?mface[i].v1: (j==1)?mface[i].v2: (j==2)?mface[i].v3: mface[i].v4; + + col[i*4+j].a = 255; + col[i*4+j].r = (char)(pPoint[index].wetness*255); + col[i*4+j].g = (char)(pPoint[index].wetness*255); + col[i*4+j].b = (char)(pPoint[index].wetness*255); + } + } + } + } + /* displace paint */ + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { + MVert *mvert = result->getVertArray(result); + int i; + float* value = (float*)surface->data->type_data; + + #pragma omp parallel for schedule(static) + for (i=0; i<surface->data->total_points; i++) { + float normal[3]; + normal_short_to_float_v3(normal, mvert[i].no); + normalize_v3(normal); + + mvert[i].co[0] -= normal[0]*value[i]; + mvert[i].co[1] -= normal[1]*value[i]; + mvert[i].co[2] -= normal[2]*value[i]; + } + + CDDM_calc_normals(result); + } + + /* vertex group paint */ + else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { + int defgrp_index = defgroup_name_index(ob, surface->output_name); + MDeformVert *dvert = result->getVertDataArray(result, CD_MDEFORMVERT); + float *weight = (float*)surface->data->type_data; + /* viewport preview */ + if (surface->flags & MOD_DPAINT_PREVIEW) { + /* Save preview results to weight layer, to be + * able to share same drawing methods */ + MFace *mface = result->getFaceArray(result); + int numOfFaces = result->getNumFaces(result); + int i; + MCol *col = result->getFaceDataArray(result, CD_WEIGHT_MCOL); + if (!col) col = CustomData_add_layer(&result->faceData, CD_WEIGHT_MCOL, CD_CALLOC, NULL, numOfFaces); + + if (col) { + #pragma omp parallel for schedule(static) + for (i=0; i<numOfFaces; i++) { + float temp_color[3]; + int j=0; + for (; j<((mface[i].v4)?4:3); j++) { + int index = (j==0)?mface[i].v1: (j==1)?mface[i].v2: (j==2)?mface[i].v3: mface[i].v4; + + col[i*4+j].a = 255; + + weight_to_rgb(weight[index], temp_color, temp_color+1, temp_color+2); + col[i*4+j].r = (char)(temp_color[2]*255); + col[i*4+j].g = (char)(temp_color[1]*255); + col[i*4+j].b = (char)(temp_color[0]*255); + } + } + pmd->canvas->flags |= MOD_DPAINT_PREVIEW_READY; + } + } + + /* apply weights into a vertex group, if doesnt exists add a new layer */ + if (defgrp_index >= 0 && !dvert && strlen(surface->output_name)>0) + dvert = CustomData_add_layer_named(&result->vertData, CD_MDEFORMVERT, CD_CALLOC, + NULL, surface->data->total_points, surface->output_name); + if (defgrp_index >= 0 && dvert) { + int i; + for(i=0; i<surface->data->total_points; i++) { + int j; + MDeformVert *dv= &dvert[i]; + MDeformWeight *def_weight = NULL; + + /* check if this vertex has a weight */ + for (j=0; j<dv->totweight; j++) { + if (dv->dw[j].def_nr == defgrp_index) { + def_weight = &dv->dw[j]; + break; + } + } + + /* if not found, add a weight for it */ + if (!def_weight) { + MDeformWeight *newdw = MEM_callocN(sizeof(MDeformWeight)*(dv->totweight+1), + "deformWeight"); + if(dv->dw){ + memcpy(newdw, dv->dw, sizeof(MDeformWeight)*dv->totweight); + MEM_freeN(dv->dw); + } + dv->dw=newdw; + dv->dw[dv->totweight].def_nr=defgrp_index; + def_weight = &dv->dw[dv->totweight]; + dv->totweight++; + } + + /* set weight value */ + def_weight->weight = weight[i]; + } + } + } + /* wave simulation */ + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + MVert *mvert = result->getVertArray(result); + int i; + PaintWavePoint* wPoint = (PaintWavePoint*)surface->data->type_data; + + #pragma omp parallel for schedule(static) + for (i=0; i<surface->data->total_points; i++) { + float normal[3]; + normal_short_to_float_v3(normal, mvert[i].no); + normalize_v3(normal); + + mvert[i].co[0] += normal[0]*wPoint[i].height; + mvert[i].co[1] += normal[1]*wPoint[i].height; + mvert[i].co[2] += normal[2]*wPoint[i].height; + } + + CDDM_calc_normals(result); + } + } + } + } + } + /* make a copy of dm to use as brush data */ + if (pmd->brush) { + if (pmd->brush->dm) pmd->brush->dm->release(pmd->brush->dm); + pmd->brush->dm = CDDM_copy(result); + } + + return result; +} + +/* Modifier call. Processes dynamic paint modifier step. */ +struct DerivedMesh *dynamicPaint_Modifier_do(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm) +{ + /* Update derived mesh data to modifier if baking */ + dynamicPaint_frameUpdate(pmd, scene, ob, dm); + + /* Return output mesh */ + return dynamicPaint_Modifier_apply(pmd, scene, ob, dm); +} + + +/***************************** Image Sequence / UV Image Canvas Calls ******************************/ + +/* +* 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(PaintUVPoint *tempPoints, DerivedMesh *dm, char *uvname, int w, int h, 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; + PaintUVPoint *tPoint = NULL; + PaintUVPoint *cPoint = NULL; + + /* shift position by given n_index */ + x = px + neighX[n_index]; + y = py + neighY[n_index]; + + if (x<0 || x>=w) return -1; + if (y<0 || y>=h) return -1; + + tPoint = &tempPoints[x+w*y]; /* UV neighbour */ + cPoint = &tempPoints[px+w*py]; /* Origin point */ + + /* + * Check if shifted point is on same face -> it's a correct neighbour + * (and if it isn't marked as an "edge pixel") + */ + if ((tPoint->face_index == cPoint->face_index) && (tPoint->neighbour_pixel == -1)) + return (x+w*y); + + /* + * Even if shifted point is on another 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->face_index != -1) && (tPoint->neighbour_pixel == -1)) { + return (x+w*y); + } + + /* + * If we get here, the actual neighbouring pixel + * is located on a non-linked uv face, and we have to find + * it's "real" position. + * + * Simple neighbouring face finding algorithm: + * - find closest uv edge to shifted pixel and get + * the another face that shares that edge + * - find corresponding position of that new face edge + * in uv space + * + * TODO: Implement something more accurate / optimized? + */ + { + int numOfFaces = dm->getNumFaces(dm); + MFace *mface = dm->getFaceArray(dm); + MTFace *tface = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, uvname); + + /* 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)w; + pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)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->face_index].uv[edge1_index], tface[cPoint->face_index].uv[edge2_index]); + + /* Dist to second edge */ + t_dist = dist_to_line_segment_v2(pixel, tface[cPoint->face_index].uv[uindex[1]], tface[cPoint->face_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->face_index].uv[uindex[2]], tface[cPoint->face_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; i<numOfFaces; i++) { + /* + * Check if both edge vertices share this face + */ + int v4 = (mface[i].v4) ? mface[i].v4 : -1; + + if ((e1_index == mface[i].v1 || e1_index == mface[i].v2 || e1_index == mface[i].v3 || e1_index == v4) && + (e2_index == mface[i].v1 || e2_index == mface[i].v2 || e2_index == mface[i].v3 || e2_index == v4)) { + if (i == cPoint->face_index) 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->face_index].uv[edge1_index]; + s_uv2 = (float *)tface[cPoint->face_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]) + 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->face_index].uv[edge1_index], tface[cPoint->face_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)w) - 0.5f; + pixel[1] = (pixel[1] * (float)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] >= w) return -1; + if (final_pixel[1] < 0 || final_pixel[1] >= h) return -1; + + final_index = final_pixel[0] + w * final_pixel[1]; + + /* If we ended up to our origin point ( mesh has smaller than pixel sized faces) */ + if (final_index == (px+w*py)) return -1; + /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ + if (tempPoints[final_index].face_index != target_face) return -1; + + /* + * If final point is an "edge pixel", use it's "real" neighbour instead + */ + if (tempPoints[final_index].neighbour_pixel != -1) final_index = cPoint->neighbour_pixel; + + return final_index; + } + } +} + +/* +* Create a surface for image sequence format +*/ +static int dynamicPaint_createUVSurface(DynamicPaintSurface *surface) +{ + /* 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}; + int ty; + int w,h; + int numOfFaces; + char uvname[32]; + int active_points = 0; + int error = 0; + + PaintSurfaceData *sData; + DynamicPaintCanvasSettings *canvas = surface->canvas; + DerivedMesh *dm = canvas->dm; + + PaintUVPoint *tempPoints = NULL; + Vec3f *tempWeights = NULL; + MVert *mvert = NULL; + MFace *mface = NULL; + MTFace *tface = NULL; + Bounds2D *faceBB = NULL; + int *final_index; + int aa_samples; + + if (!dm) return printError(canvas, "Canvas mesh not updated."); + if (surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ) return printError(canvas, "Can't bake non-\"image sequence\" formats."); + + numOfFaces = dm->getNumFaces(dm); + mvert = dm->getVertArray(dm); + mface = dm->getFaceArray(dm); + + /* get uv layer */ + validate_layer_name(&dm->faceData, CD_MTFACE, surface->uvlayer_name, uvname); + tface = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, uvname); + + /* Check for validity */ + if (!tface) return printError(canvas, "No UV data on canvas."); + if (surface->image_resolution < 16 || surface->image_resolution > 8096) return printError(canvas, "Invalid resolution."); + + w = h = surface->image_resolution; + + /* + * Start generating the surface + */ + printf("DynamicPaint: Preparing UV surface of %ix%i pixels and %i faces.\n", w, h, numOfFaces); + + /* Init data struct */ + if (surface->data) dynamicPaint_freeSurfaceData(surface); + sData = surface->data = MEM_callocN(sizeof(PaintSurfaceData), "PaintSurfaceData"); + if (!surface->data) return printError(canvas, "Not enough free memory."); + + aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + tempPoints = (struct PaintUVPoint *) MEM_callocN(w*h*sizeof(struct PaintUVPoint), "Temp PaintUVPoint"); + if (!tempPoints) error=1; + + final_index = (int *) MEM_callocN(w*h*sizeof(int), "Temp UV Final Indexes"); + if (!final_index) error=1; + + if (!error) { + tempWeights = (struct Vec3f *) MEM_mallocN(w*h*aa_samples*sizeof(struct Vec3f), "Temp bWeights"); + if (!tempWeights) error=1; + } + + /* + * Generate a temporary bounding box array for UV faces to optimize + * the pixel-inside-a-face search. + */ + if (!error) { + faceBB = (struct Bounds2D *) MEM_mallocN(numOfFaces*sizeof(struct Bounds2D), "MPCanvasFaceBB"); + if (!faceBB) error=1; + } + + if (!error) + for (ty=0; ty<numOfFaces; ty++) { + int numOfVert = (mface[ty].v4) ? 4 : 3; + int i; + + VECCOPY2D(faceBB[ty].min, tface[ty].uv[0]); + VECCOPY2D(faceBB[ty].max, tface[ty].uv[0]); + + for (i = 1; i<numOfVert; i++) { + if (tface[ty].uv[i][0] < faceBB[ty].min[0]) faceBB[ty].min[0] = tface[ty].uv[i][0]; + if (tface[ty].uv[i][1] < faceBB[ty].min[1]) faceBB[ty].min[1] = tface[ty].uv[i][1]; + if (tface[ty].uv[i][0] > faceBB[ty].max[0]) faceBB[ty].max[0] = tface[ty].uv[i][0]; + if (tface[ty].uv[i][1] > faceBB[ty].max[1]) faceBB[ty].max[1] = tface[ty].uv[i][1]; + + } + } + + /* + * Loop through every pixel and check + * if pixel is uv-mapped on a canvas face. + */ + if (!error) { + #pragma omp parallel for schedule(static) + for (ty = 0; ty < h; ty++) + { + int tx; + for (tx = 0; tx < w; tx++) + { + int i, sample; + int index = tx+w*ty; + PaintUVPoint *tPoint = (&tempPoints[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 */ + tPoint->face_index = -1; + tPoint->neighbour_pixel = -1; + tPoint->pixel_index = index; + + /* Actual pixel center, used when collision is found */ + point[0][0] = ((float)tx + 0.5f) / w; + point[0][1] = ((float)ty + 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)tx) / w; + point[1][1] = ((float)ty) / h; + + point[2][0] = ((float)tx+1) / w; + point[2][1] = ((float)ty) / h; + + point[3][0] = ((float)tx) / w; + point[3][1] = ((float)ty+1) / h; + + point[4][0] = ((float)tx+1) / w; + point[4][1] = ((float)ty+1) / h; + + + /* Loop through samples, starting from middle point */ + for (sample=0; sample<5; sample++) { + + /* Loop through every face in the mesh */ + for (i=0; i<numOfFaces; i++) { + + /* Check uv bb */ + if (faceBB[i].min[0] > (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; j<aa_samples; 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, tempWeights[index*aa_samples+j].v); + } + + /* Set surface point face values */ + tPoint->face_index = i; /* face index */ + tPoint->quad = (isInside == 2) ? 1 : 0; /* quad or tri part*/ + + /* save vertex indexes */ + tPoint->v1 = (isInside == 2) ? mface[i].v1 : mface[i].v1; + tPoint->v2 = (isInside == 2) ? mface[i].v3 : mface[i].v2; + tPoint->v3 = (isInside == 2) ? mface[i].v4 : mface[i].v3; + + sample = 5; /* make sure we exit sample loop as well */ + break; + } + } + } /* sample 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 (ty = 0; ty < h; ty++) + { + int tx; + for (tx = 0; tx < w; tx++) + { + int index = tx+w*ty; + PaintUVPoint *tPoint = (&tempPoints[index]); + + /* If point isnt't on canvas mesh */ + if (tPoint->face_index == -1) { + int u_min, u_max, v_min, v_max; + int u,v, ind; + float point[2]; + + /* get loop area */ + u_min = (tx > 0) ? -1 : 0; + u_max = (tx < (w-1)) ? 1 : 0; + v_min = (ty > 0) ? -1 : 0; + v_max = (ty < (h-1)) ? 1 : 0; + + point[0] = ((float)tx + 0.5f) / w; + point[1] = ((float)ty + 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 = (tx+u)+w*(ty+v); + + /* if neighbour has index */ + if (tempPoints[ind].face_index != -1) { + + float uv1co[2], uv2co[2], uv3co[2], uv[2]; + int i = tempPoints[ind].face_index, j; + + /* Now calculate pixel data for this pixel as it was on polygon surface */ + if (!tempPoints[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; j<aa_samples; 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, tempWeights[index*aa_samples+j].v); + } + + /* Set values */ + tPoint->neighbour_pixel = ind; // face index + tPoint->quad = tempPoints[ind].quad; // quad or tri + + /* save vertex indexes */ + tPoint->v1 = (tPoint->quad) ? mface[i].v1 : mface[i].v1; + tPoint->v2 = (tPoint->quad) ? mface[i].v3 : mface[i].v2; + tPoint->v3 = (tPoint->quad) ? mface[i].v4 : mface[i].v3; + + u = u_max + 1; /* make sure we exit outer loop as well */ + break; + } + } + } + } + } + } + + /* + * When base loop is over convert found neighbour indexes to real ones + * Also count the final number of active surface points + */ + for (ty = 0; ty < h; ty++) + { + int tx; + for (tx = 0; tx < w; tx++) + { + int index = tx+w*ty; + PaintUVPoint *tPoint = (&tempPoints[index]); + + if (tPoint->face_index == -1 && tPoint->neighbour_pixel != -1) tPoint->face_index = tempPoints[tPoint->neighbour_pixel].face_index; + if (tPoint->face_index != -1) active_points++; + } + } + + /* If any effect enabled, create surface effect / wet layer + * neighbour lists. Processes possibly moving data. */ + if (surface_usesAdjData(surface)) { + + int i, cursor=0; + + /* Create a temporary array of final indexes (before unassigned + * pixels have been dropped) */ + for (i=0; i<w*h; i++) { + if (tempPoints[i].face_index != -1) { + final_index[i] = cursor; + cursor++; + } + } + /* allocate memory */ + sData->total_points = w*h; + dynamicPaint_initAdjacencyData(surface); + + if (sData->adj_data) { + PaintAdjData *ed = sData->adj_data; + unsigned int n_pos = 0; + //#pragma omp parallel for schedule(static) + for (ty = 0; ty < h; ty++) + { + int tx; + for (tx = 0; tx < w; tx++) + { + int i, index = tx+w*ty; + + if (tempPoints[index].face_index != -1) { + ed->n_index[final_index[index]] = n_pos; + ed->n_num[final_index[index]] = 0; + + for (i=0; i<8; i++) { + + /* Try to find a neighbouring pixel in defined direction + * If not found, -1 is returned */ + int n_target = dynamicPaint_findNeighbourPixel(tempPoints, dm, uvname, w, h, tx, ty, i); + + if (n_target != -1) { + ed->n_target[n_pos] = final_index[n_target]; + ed->n_num[final_index[index]]++; + n_pos++; + } + } + } + } + } + } + } + + /* Create final surface data without inactive points */ + { + ImgSeqFormatData *f_data = MEM_callocN(sizeof(struct ImgSeqFormatData), "ImgSeqFormatData"); + if (f_data) { + f_data->uv_p = MEM_callocN(active_points*sizeof(struct PaintUVPoint), "PaintUVPoint"); + f_data->barycentricWeights = MEM_callocN(active_points*aa_samples*sizeof(struct Vec3f), "PaintUVPoint"); + + if (!f_data->uv_p || !f_data->barycentricWeights) error=1; + } + else error=1; + + sData->total_points = active_points; + + /* in case of allocation error, free everything */ + if (error) { + if (f_data) { + if (f_data->uv_p) MEM_freeN(f_data->uv_p); + if (f_data->barycentricWeights) MEM_freeN(f_data->barycentricWeights); + MEM_freeN(f_data); + } + } + else { + int index, cursor = 0; + sData->total_points = active_points; + sData->format_data = f_data; + + for(index = 0; index < (w*h); index++) { + if (tempPoints[index].face_index != -1) { + memcpy(&f_data->uv_p[cursor], &tempPoints[index], sizeof(PaintUVPoint)); + memcpy(&f_data->barycentricWeights[cursor*aa_samples], &tempWeights[index*aa_samples], sizeof(Vec3f)*aa_samples); + cursor++; + } + } + } + } + } + if (error==1) printError(canvas, "Not enough free memory."); + + if (faceBB) MEM_freeN(faceBB); + if (tempPoints) MEM_freeN(tempPoints); + if (tempWeights) MEM_freeN(tempWeights); + if (final_index) MEM_freeN(final_index); + + /* Init surface type data */ + if (!error) { + dynamicPaint_allocateSurfaceType(surface); + +#if 0 + /* ----------------------------------------------------------------- + * For debug, output pixel statuses to the color map + * -----------------------------------------------------------------*/ + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + ImgSeqFormatData *f_data = (ImgSeqFormatData*)sData->format_data; + PaintUVPoint *uvPoint = &((PaintUVPoint*)f_data->uv_p)[index]; + PaintPoint *pPoint = &((PaintPoint*)sData->type_data)[index]; + pPoint->alpha=1.0f; + + /* Every pixel that is assigned as "edge pixel" gets blue color */ + if (uvPoint->neighbour_pixel != -1) pPoint->color[2] = 1.0f; + /* and every pixel that finally got an polygon gets red color */ + if (uvPoint->face_index != -1) pPoint->color[0] = 1.0f; + /* green color shows pixel face index hash */ + if (uvPoint->face_index != -1) pPoint->color[1] = (float)(uvPoint->face_index % 255)/256.0f; + } + +#endif + } + + return (error == 0); +} + +#define DPOUTPUT_PAINT 0 +#define DPOUTPUT_WET 1 +#define DPOUTPUT_DISPLACE 2 +#define DPOUTPUT_WAVES 3 + +/* +* Outputs an image file from uv surface data. +*/ +void dynamicPaint_outputImage(DynamicPaintSurface *surface, char* filename, short format, short type) +{ + int index; + ImBuf* mhImgB = NULL; + PaintSurfaceData *sData = surface->data; + ImgSeqFormatData *f_data = (ImgSeqFormatData*)sData->format_data; + char output_file[250]; + + if (sData == NULL || sData->type_data == NULL) {printError(surface->canvas, "Image save failed: Invalid surface.");return;} + + if (format == DPOUTPUT_JPEG) sprintf(output_file,"%s.jpg",filename); + else if (format == DPOUTPUT_OPENEXR) sprintf(output_file,"%s.exr",filename); + else sprintf(output_file,"%s.png",filename); + + /* Validate output file path */ + BLI_path_abs(output_file, G.main->name); + BLI_make_existing_file(output_file); + + /* Init image buffer */ + mhImgB = IMB_allocImBuf(surface->image_resolution, surface->image_resolution, 32, IB_rectfloat); + if (mhImgB == NULL) {printError(surface->canvas, "Image save failed: Not enough free memory.");return;} + + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + int pos=f_data->uv_p[index].pixel_index*4; /* image buffer position */ + + + /* Set values of preferred type */ + if (type == DPOUTPUT_WET) { + PaintPoint *point = &((PaintPoint*)sData->type_data)[index]; + float value = (point->wetness > 1.0f) ? 1.0f : point->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 (type == DPOUTPUT_PAINT) { + PaintPoint *point = &((PaintPoint*)sData->type_data)[index]; + + mhImgB->rect_float[pos] = point->color[0]; + mhImgB->rect_float[pos+1] = point->color[1]; + mhImgB->rect_float[pos+2] = point->color[2]; + /* mix wet layer */ + if (point->e_alpha) mixColors(&mhImgB->rect_float[pos], point->alpha, point->e_color, point->e_alpha); + + /* use highest alpha */ + mhImgB->rect_float[pos+3] = (point->e_alpha > point->alpha) ? point->e_alpha : point->alpha; + + /* Multiply color by alpha if enabled */ + if (surface->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]; + } + } + else if (type == DPOUTPUT_DISPLACE) { + float depth = ((float*)sData->type_data)[index]; + + if (surface->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 if (type == DPOUTPUT_WAVES) { + PaintWavePoint *wPoint = &((PaintWavePoint*)sData->type_data)[index]; + float depth = wPoint->height/2.0f+0.5f; + + mhImgB->rect_float[pos]=depth; + mhImgB->rect_float[pos+1]=depth; + mhImgB->rect_float[pos+2]=depth; + mhImgB->rect_float[pos+3]=1.0f; + } + } + + /* Save image buffer */ + if (format == DPOUTPUT_JPEG) { /* JPEG */ + mhImgB->ftype= JPG|95; + IMB_rect_from_float(mhImgB); + imb_savejpeg(mhImgB, output_file, IB_rectfloat); + } +#ifdef WITH_OPENEXR + else if (format == DPOUTPUT_OPENEXR) { /* OpenEXR 32-bit float */ + mhImgB->ftype = OPENEXR | OPENEXR_COMPRESS; + IMB_rect_from_float(mhImgB); + imb_save_openexr(mhImgB, output_file, IB_rectfloat); + } +#endif + else { /* DPOUTPUT_PNG */ + mhImgB->ftype= PNG|95; + IMB_rect_from_float(mhImgB); + imb_savepng(mhImgB, output_file, IB_rectfloat); + } + + IMB_freeImBuf(mhImgB); +} + + +/***************************** Material / Texture Sampling ******************************/ + +/* +* Update animated textures and 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, Scene *scene) +{ + MTex *mtex = NULL; + Tex *tex = NULL; + int tex_nr; + if (mat == NULL) return; + + /* update material anims */ + BKE_animsys_evaluate_animdata(&mat->id, mat->adt, BKE_curframe(scene), ADT_RECALC_ANIM); + + /* + * Loop through every material texture and check + * if they are mapped by other object + */ + for(tex_nr=0; tex_nr<MAX_MTEX; tex_nr++) { + /* separate tex switching */ + if(mat->septex & (1<<tex_nr)) continue; + + if(mat->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); + } + } + + /* update texture anims */ + BKE_animsys_evaluate_animdata(&tex->id, tex->adt, BKE_curframe(scene), ADT_RECALC_ANIM); + + /* update cache if voxel data */ + if(tex->id.us && tex->type==TEX_VOXELDATA) { + cache_voxeldata(tex, (int)scene->r.cfra); + } + if(tex->id.us && tex->type==TEX_POINTDENSITY) { + /* set dummy values for render used */ + Render dummy_re = {0}; + dummy_re.scene = scene; + unit_m4(dummy_re.viewinv); + unit_m4(dummy_re.viewmat); + unit_m4(dummy_re.winmat); + dummy_re.winx = 128; + dummy_re.winy = 128; + cache_pointdensity(&dummy_re, tex); + } + + /* update image sequences and movies */ + if(tex->ima && ELEM(tex->ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { + if(tex->iuser.flag & IMA_ANIM_ALWAYS) + BKE_image_user_calc_frame(&tex->iuser, (int)scene->r.cfra, 0); + } + } + } +} + +/* Initialize materials for object: +* Calculates inverce matrices for linked objects, updates +* volume caches etc. */ +static void dynamicPaint_updateObjectMaterials(Object *brushOb, Material *ui_mat, Scene *scene) +{ + /* Calculate inverse transformation matrix + * for this object */ + invert_m4_m4(brushOb->imat, brushOb->obmat); + + /* Now process every material linked to this brush object */ + if ((ui_mat == NULL) && brushOb->mat && brushOb->totcol) { + int i, tot=(*give_totcolp(brushOb))+1; + for (i=1; i<tot; i++) { + dynamicPaint_updateMaterial(give_current_material(brushOb,i), scene); + } + } + else { + dynamicPaint_updateMaterial(ui_mat, scene); + } +} + +/* A modified part of shadeinput.c -> shade_input_set_uv() / shade_input_set_shade_texco() +* Used for sampling UV mapped texture color */ +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() +* Used for sampling UV mapped texture color */ +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 *brushOb, 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(brushOb->imat, xyz_local); + + for(tex_nr=0; tex_nr<MAX_MTEX; tex_nr++) { + + /* separate tex switching */ + if(mat->septex & (1<<tex_nr)) continue; + + if(mat->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(brushOb->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 *brushOb, 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_nr<MAX_MTEX; tex_nr++) { + + /* separate tex switching */ + if(mat->septex & (1<<tex_nr)) continue; + + if(mat->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= brushOb; + 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 diffuse color and alpha (including linked textures) in given coordinates +* +* color,paint : input/output color values +* pixelCoord : canvas pixel coordinates in global space. used if material is volumetric +* paintHit : ray hit point on paint object surface in global space. used by "surface" type materials +* faceIndex : ray hit face index +* orcoDm : orco state derived mesh of paint object +* ui_mat : force material. if NULL, material linked to mesh face is used. +* +* *"brush object" = object to sample material color from +*/ +void dynamicPaint_getMaterialColor(float *color, float *alpha, Object *brushOb, 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(brushOb, 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, brushOb, paintHit, faceIndex, isQuad, orcoDm); + } + else if (material->material_type == MA_TYPE_VOLUME) { + /* Volumetric material */ + dynamicPaint_sampleVolumeMaterial(color, alpha, material, brushOb, pixelCoord); + } + else if (material->material_type == MA_TYPE_HALO) { + /* Halo type not supported */ + } +} + + +/***************************** Ray / Nearest Point Utils ******************************/ + + +/* 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 brush 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 = ray_tri_intersection(ray, hit->dist, t0, t1, t2); + + if(dist >= 0 && dist < hit->dist) + { + hit->index = index; + hit->dist = dist; + 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 brush 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); + nearest->no[0] = (quad) ? 1.0f : 0.0f; + } + + t1 = t2; + t2 = t3; + t3 = NULL; + quad = 1; + + } while(t2); +} + + +/***************************** Painting Calls ******************************/ + +/* +* Mix color values to canvas point. +* +* surface : canvas surface +* index : surface point index +* paintFlags : paint object flags +* paintColor,Alpha,Wetness : to be mixed paint values +* timescale : value used to adjust time dependand +* operations when using substeps +*/ +static void dynamicPaint_mixPaintColors(DynamicPaintSurface *surface, int index, int paintFlags, float *paintColor, float *paintAlpha, float *paintWetness, float *timescale) +{ + PaintPoint *pPoint = &((PaintPoint*)surface->data->type_data)[index]; + + /* Add paint */ + if (!(paintFlags & MOD_DPAINT_ERASE)) { + float wetness; + float temp_alpha = (*paintAlpha) * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : (*timescale)); + + /* mix brush color with wet layer color */ + if (temp_alpha) mixColors(pPoint->e_color, pPoint->e_alpha, paintColor, temp_alpha); + + /* alpha */ + if (paintFlags & MOD_DPAINT_ABS_ALPHA) { + if (pPoint->e_alpha < (*paintAlpha)) pPoint->e_alpha = (*paintAlpha); + } + else { + pPoint->e_alpha += temp_alpha; + if (pPoint->e_alpha > 1.0f) pPoint->e_alpha = 1.0f; + } + + /* only increase wetness if it's below paint level */ + wetness = (*paintWetness) * pPoint->e_alpha; + if (pPoint->wetness < wetness) pPoint->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 = (pPoint->e_alpha > pPoint->alpha) ? pPoint->e_alpha : pPoint->alpha; + if (a_highest > invFact) { + a_ratio = invFact / a_highest; + + pPoint->e_alpha *= a_ratio; + pPoint->alpha *= a_ratio; + } + } + else { + pPoint->e_alpha -= (*paintAlpha) * (*timescale); + if (pPoint->e_alpha < 0.0f) pPoint->e_alpha = 0.0f; + pPoint->alpha -= (*paintAlpha) * (*timescale); + if (pPoint->alpha < 0.0f) pPoint->alpha = 0.0f; + } + + wetness = (1.0f - (*paintWetness)) * pPoint->e_alpha; + if (pPoint->wetness > wetness) pPoint->wetness = wetness; + } +} + +static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrushSettings *brush, float isect_height) +{ + int hit = 0; + isect_height *= brush->wave_factor; + + /* determine hit depending on wave_factor */ + if (brush->wave_factor > 0.0f && wPoint->height > isect_height) + hit = 1; + else if (brush->wave_factor < 0.0f && wPoint->height < isect_height) + hit = 1; + + if (hit) { + if (brush->wave_type == MOD_DPAINT_WAVEB_DEPTH) { + wPoint->height = isect_height; + wPoint->state = 1; + wPoint->velocity = 0.0f; + } + else if (brush->wave_type == MOD_DPAINT_WAVEB_FORCE) + wPoint->velocity = isect_height; + else if (brush->wave_type == MOD_DPAINT_WAVEB_REFLECT) + wPoint->state = 2; + } +} + +/* +* add paint results to the surface data depending on surface type +*/ +static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned int index, DynamicPaintBrushSettings *brush, float paint[4], float strength, float depth, float timescale) +{ + PaintSurfaceData *sData = surface->data; + + strength *= brush->alpha; + + /* mix paint surface */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + + float paintWetness = brush->wetness * strength; + float paintAlpha = strength; + if (paintAlpha > 1.0f) paintAlpha = 1.0f; + + dynamicPaint_mixPaintColors(surface, index, brush->flags, paint, &paintAlpha, &paintWetness, ×cale); + + } + /* displace surface */ + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { + float *value = (float*)sData->type_data; + + if (brush->flags & MOD_DPAINT_ERASE) { + value[index] *= (1.0f - strength); + if (value[index] < 0.0f) value[index] = 0.0f; + } + else { + if (value[index] < depth) value[index] = depth; + } + } + /* vertex weight group surface */ + else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { + float *value = (float*)sData->type_data; + + if (brush->flags & MOD_DPAINT_ERASE) { + value[index] *= (1.0f - strength); + if (value[index] < 0.0f) value[index] = 0.0f; + } + else { + if (value[index] < strength) value[index] = strength; + } + } + /* wave surface */ + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + dynamicPaint_mixWaveHeight(&((PaintWavePoint*)sData->type_data)[index], + brush, 0.0f-depth); + } +} + +static int meshBrush_boundsIntersect(Bounds3D *b1, Bounds3D *b2, DynamicPaintBrushSettings *brush) +{ + if (brush->collision == MOD_DPAINT_COL_VOLUME) + return boundsIntersect(b1, b2); + else if (brush->collision == MOD_DPAINT_COL_DIST || brush->collision == MOD_DPAINT_COL_VOLDIST) + return boundsIntersectDist(b1, b2, brush->paint_distance); + else return 1; +} + +#define VECADDVAL(v,val) {*(v)+=(val); *(v+1)+=(val); *(v+2)+=(val);} +#define VECMULVAL(v,val) {*(v)*=(val); *(v+1)*=(val); *(v+2)*=(val);} + +#define HIT_VOLUME 1 +#define HIT_PROXIMITY 2 + +/* +* Paint a brush object mesh to the surface +*/ +static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, DynamicPaintBrushSettings *brush, Object *canvasOb, Object *brushOb, float timescale) +{ + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + DerivedMesh *dm = NULL; + MVert *mvert = NULL; + MFace *mface = NULL; + + if (!brush->dm) return 0; + + { + BVHTreeFromMesh treeData = {0}; + + int numOfVerts; + int ii; + + Bounds3D mesh_bb; + VolumeGrid *grid = bData->grid; + + /* + * Transform collider vertices to world space + * (Faster than transforming per pixel + * coordinates and normals to object space) + */ + dm = CDDM_copy(brush->dm); + mvert = dm->getVertArray(dm); + mface = dm->getFaceArray(dm); + numOfVerts = dm->getNumVerts(dm); + + for (ii=0; ii<numOfVerts; ii++) { + mul_m4_v3(brushOb->obmat, mvert[ii].co); + + if (!ii) { + VECCOPY(mesh_bb.min, mvert[ii].co); + VECCOPY(mesh_bb.max, mvert[ii].co); + } + else + boundInsert(&mesh_bb, mvert[ii].co); + } + + /* check bounding box collision */ + if(grid && meshBrush_boundsIntersect(&grid->grid_bounds, &mesh_bb, brush)) + /* Build a bvh tree from transformed vertices */ + if (bvhtree_from_mesh_faces(&treeData, dm, 0.0f, 4, 8)) + { + int c_index; + int total_cells = grid->x*grid->y*grid->z; + + /* loop through space partitioning grid */ + for (c_index=0; c_index<total_cells; c_index++) { + int id; + + /* check cell bounding box */ + if (!grid->s_num[c_index] || + !meshBrush_boundsIntersect(&grid->bounds[c_index], &mesh_bb, brush)) + continue; + + /* loop through cell points */ + #pragma omp parallel for schedule(static) + for (id = 0; id < grid->s_num[c_index]; id++) + { + int index = grid->t_index[grid->s_pos[c_index] + id]; + + /*float color[3] = {1.0f, 0.0f, 0.0f}; + dynamicPaint_updatePointData(surface, index, brush, color, 1.0f, 0.0f, timescale); + continue;*/ + { + int ss, samples = bData->s_num[index]; + float total_sample = (float)samples; + float brushStrength = 0.0f; /* brush influence factor */ + float depth = 0.0f; /* displace depth */ + + float paintColor[3] = {0.0f}; + int numOfHits = 0; + + /* for image sequence anti-aliasing, use gaussian factors */ + if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + total_sample = gaussianTotal; + + /* Supersampling */ + for (ss=0; ss<samples; ss++) + { + + float ray_start[3], ray_dir[3] = {0.0f}; + float colorband[4] = {0.0f}; + float sample_factor; + float sampleStrength = 0.0f; + BVHTreeRayHit hit; + BVHTreeNearest nearest; + short hit_found = 0; + + /* 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 (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + sample_factor = gaussianFactors[ss]; + else + sample_factor = 1.0f; + + /* Get current sample position in world coordinates */ + VECCOPY(ray_start, bData->realCoord[bData->s_pos[index]+ss].v); + if (!(brush->flags & MOD_DPAINT_ACCEPT_NONCLOSED) || brush->ray_dir == MOD_DPAINT_RAY_CANVAS) { + VECCOPY(ray_dir, bData->bNormal[index].invNorm); + } + else /* MOD_DPAINT_RAY_ZPLUS */ + ray_dir[2] = 1.0f; + + /* a simple hack to minimize change of ray leaks at identical ray - edge locations */ + VECADDVAL(ray_start, 0.001f); + + hit.index = -1; + hit.dist = 9999; + nearest.index = -1; + nearest.dist = brush->paint_distance * brush->paint_distance; /* find_nearest search uses squared distance */ + + /* Check volume collision */ + if (brush->collision == MOD_DPAINT_COL_VOLUME || brush->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) + { + float dist = hit.dist; + int f_index = hit.index; + + /* In case of non-closed volumes, also cast a ray in opposite direction + * to make sure point is at least between two brush faces*/ + if (!(brush->flags & MOD_DPAINT_ACCEPT_NONCLOSED)) { + VECMULVAL(ray_dir, -1.0f); + hit.index = -1; + hit.dist = 9999; + + BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, &hit, mesh_faces_spherecast_dp, &treeData); + } + + if(hit.index != -1) { + /* Add factor on supersample filter */ + sampleStrength += sample_factor; + hit_found = HIT_VOLUME; + + /* + * Mark hit info + */ + VECADDFAC(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ + depth += dist*sample_factor; + hitFace = f_index; + hitQuad = quad; + } + } + } // end of raycast + + /* Check proximity collision */ + if ((brush->collision == MOD_DPAINT_COL_DIST || brush->collision == MOD_DPAINT_COL_VOLDIST) && + (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX))) + { + float proxDist = -1.0f; + float hitCo[3]; + short hQuad; + int face; + + /* if inverse prox and no hit found, skip this sample */ + if (brush->flags & MOD_DPAINT_INVERSE_PROX && !hit_found) continue; + + /* + * If pure distance proximity, find the nearest point on the mesh + */ + if (!(brush->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 = brush->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 && proxDist <= brush->paint_distance) { + float dist_rate = proxDist / brush->paint_distance; + float prox_influence = 0.0f; + + /* in case of inverse prox also undo volume effect */ + if (brush->flags & MOD_DPAINT_INVERSE_PROX) { + sampleStrength -= sample_factor; + dist_rate = 1.0f - dist_rate; + } + + /* if using color ramp*/ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && do_colorband(brush->paint_ramp, dist_rate, colorband)) + prox_influence = colorband[3]; + /* if using smooth falloff, multiply gaussian factor */ + else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH) { + prox_influence = (1.0f - dist_rate) * sample_factor; + } + else prox_influence = (brush->flags & MOD_DPAINT_INVERSE_PROX) ? 0.0f : 1.0f; + + hit_found = HIT_PROXIMITY; + sampleStrength += prox_influence*sample_factor; + + /* if no volume hit, use prox point face info */ + if (hitFace == -1) { + copy_v3_v3(hitCoord, hitCo); + hitQuad = hQuad; + hitFace = face; + } + } + } + + if (!hit_found) continue; + + /* + * Process hit color and alpha + */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) + { + float sampleColor[3]; + float alpha_factor = 1.0f; + + sampleColor[0] = brush->r; + sampleColor[1] = brush->g; + sampleColor[2] = brush->b; + + /* Get material+textures color on hit point if required */ + if (brush->flags & MOD_DPAINT_USE_MATERIAL) dynamicPaint_getMaterialColor(sampleColor, &alpha_factor, brushOb, bData->realCoord[bData->s_pos[index]+ss].v, hitCoord, hitFace, hitQuad, brush->dm, brush->mat); + + /* Sample colorband if required */ + if ((hit_found == HIT_PROXIMITY) && (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) { + if (!(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { + sampleColor[0] = colorband[0]; + sampleColor[1] = colorband[1]; + sampleColor[2] = colorband[2]; + } + } + + /* Add AA sample */ + paintColor[0] += sampleColor[0]; + paintColor[1] += sampleColor[1]; + paintColor[2] += sampleColor[2]; + sampleStrength *= alpha_factor; + numOfHits++; + } + + /* apply sample strength */ + brushStrength += sampleStrength; + } // end supersampling + + + /* if any sample was inside paint range */ + if (brushStrength > 0.01f) { + + /* apply supersampling results */ + if (samples > 1) { + brushStrength /= total_sample; + } + CLAMP(brushStrength, 0.0f, 1.0f); + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + /* Get final pixel color and alpha */ + paintColor[0] /= numOfHits; + paintColor[1] /= numOfHits; + paintColor[2] /= numOfHits; + paintColor[3] /= total_sample; + } + /* get final object space depth */ + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + depth /= bData->bNormal[index].normal_scale * total_sample; + } + + dynamicPaint_updatePointData(surface, index, brush, paintColor, brushStrength, depth, timescale); + } + } + } + } + } + /* free bhv tree */ + free_bvhtree_from_mesh(&treeData); + dm->release(dm); + + } + + return 1; +} + +/* +* Paint a particle system to the surface +*/ +static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, ParticleSystem *psys, DynamicPaintBrushSettings *brush, Object *canvasOb, float timescale) +{ + ParticleSettings *part=psys->part; + ParticleData *pa = NULL; + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + VolumeGrid *grid = bData->grid; + + KDTree *tree; + int particlesAdded = 0; + int invalidParticles = 0; + int p = 0; + + float solidradius = (brush->flags & MOD_DPAINT_PART_RAD) ? psys->part->size : brush->particle_radius; + float smooth = brush->particle_smooth; + + float range = solidradius + smooth; + + Bounds3D part_bb; + + 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; p<psys->totpart; 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;} + + /* make sure particle is close enough to canvas */ + if (!boundIntersectPoint(&grid->grid_bounds, pa->state.co, range)) continue; + + BLI_kdtree_insert(tree, p, pa->state.co, NULL); + + /* calc particle system bounds */ + if (particlesAdded == 0) { + VECCOPY(part_bb.min, pa->state.co); + VECCOPY(part_bb.max, pa->state.co); + } + else + boundInsert(&part_bb, pa->state.co); + + 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; + } + + /* only continue if particle bb is close enough to canvas bb */ + if (boundsIntersectDist(&grid->grid_bounds, &part_bb, range)) + { + int c_index; + int total_cells = grid->x*grid->y*grid->z; + int num_of_threads = 1; + + /* nearest particles search array for each thread */ + KDTreeNearest **nearest_th = NULL; + int nearest_limit = particlesAdded/10; + if (nearest_limit<10) nearest_limit = 10; + + /* balance tree */ + BLI_kdtree_balance(tree); + +#ifdef _OPENMP + num_of_threads = omp_get_max_threads(); +#endif + + nearest_th = MEM_callocN((num_of_threads)*sizeof(KDTreeNode*), "nearest_for_threads"); + + if (brush->flags & MOD_DPAINT_PART_RAD) + for (c_index=0; c_index<num_of_threads; c_index++) { + nearest_th[c_index] = MEM_callocN((nearest_limit)*sizeof(KDTreeNode), "dp_psys_treefoundstack"); + } + + /* loop through space partitioning grid */ + for (c_index=0; c_index<total_cells; c_index++) { + int id; + + /* check cell bounding box */ + if (!grid->s_num[c_index] || + !boundsIntersectDist(&grid->bounds[c_index], &part_bb, range)) + continue; + + /* loop through cell points */ + #pragma omp parallel for schedule(static) + for (id = 0; id < grid->s_num[c_index]; id++) + { + int index = grid->t_index[grid->s_pos[c_index] + id]; + float disp_intersect = 0.0f; + float radius = 0.0f; + float strength = 0.0f; + + /* + * 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 */ + BLI_kdtree_find_nearest(tree, bData->realCoord[bData->s_pos[index]].v, 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 using random per particle radius and closest particle didn't give max influence */ + if (brush->flags & MOD_DPAINT_PART_RAD && strength < 1.0f && psys->part->randsize > 0.0f) { + /* + * If we use per particle radius, we have to sample all particles + * within max radius range + */ +#ifdef _OPENMP + KDTreeNearest *nearest = nearest_th[omp_get_thread_num()]; +#else + KDTreeNearest *nearest = nearest_th[0]; +#endif + int n, particles = 0; + float smooth_range = range, dist; + /* calculate max range that can have particles with higher influence than the nearest one */ + float max_range = smooth - strength*smooth + solidradius; + + particles = BLI_kdtree_range_search_thread_safe(tree, max_range, bData->realCoord[bData->s_pos[index]].v, NULL, nearest, nearest_limit); + + /* Find particle that produces highest influence */ + for(n=0; n<particles; n++) { + ParticleData *pa = psys->particles + nearest[n].index; + float s_range; + + /* skip if out of range */ + if (nearest[n].dist > (pa->size + smooth)) + continue; + + /* update hit data */ + s_range = nearest[n].dist - pa->size; + + /* continue if higher influence is already found */ + if (smooth_range < s_range) + continue; + + /* update hit data */ + smooth_range = s_range; + dist = nearest[n].dist; + + /* If inside solid range and no disp depth required, no need to seek further */ + if (s_range < 0.0f) + if (surface->type != MOD_DPAINT_SURFACE_T_DISPLACE && + surface->type != MOD_DPAINT_SURFACE_T_WAVE) + break; + } + + /* now calculate influence for this particle */ + if (smooth_range != range) { + float rad = radius + smooth, str; + + if ((rad-dist) > disp_intersect) { + disp_intersect = dist; + radius = rad; + } + + /* 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; + } + + } + + if (strength > 0.001f) + { + float paintColor[4] = {0.0f}; + float depth = 0.0f; + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + paintColor[0] = brush->r; + paintColor[1] = brush->g; + paintColor[2] = brush->b; + } + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + /* get displace depth */ + disp_intersect = (1.0f - sqrt(disp_intersect / radius)) * radius; + depth = (radius - disp_intersect) / bData->bNormal[index].normal_scale; + if (depth<0.0f) depth = 0.0f; + } + + dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, timescale); + } + } + } + + /* free nearest array */ + if (brush->flags & MOD_DPAINT_PART_RAD) + for (c_index=0; c_index<num_of_threads; c_index++) { + if (nearest_th[c_index]) MEM_freeN(nearest_th[c_index]); + } + if (nearest_th) MEM_freeN(nearest_th); + } + BLI_kdtree_free(tree); + + return 1; +} + +static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush, Object *canvasOb, float timescale) +{ + int index; + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + + /* + * Loop through every surface point + */ + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + float distance = len_v3v3(pointCoord, bData->realCoord[bData->s_pos[index]].v); + float colorband[4] = {0.0f}; + float strength; + + if (distance>brush->paint_distance) continue; + + /* Smooth range or color ramp */ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH || + brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) { + + strength = 1.0f - distance / brush->paint_distance; + CLAMP(strength, 0.0f, 1.0f); + } + else strength = 1.0f; + + if (strength >= 0.001f) { + float paintColor[3] = {0.0f}; + float depth = 0.0f; + + /* color ramp */ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && do_colorband(brush->paint_ramp, (1.0f-strength), colorband)) + strength = colorband[3]; + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) { + paintColor[0] = colorband[0]; + paintColor[1] = colorband[1]; + paintColor[2] = colorband[2]; + } + else { + paintColor[0] = brush->r; + paintColor[1] = brush->g; + paintColor[2] = brush->b; + } + } + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + /* get displace depth */ + float disp_intersect = (1.0f - sqrt((brush->paint_distance-distance) / brush->paint_distance)) * brush->paint_distance; + depth = (brush->paint_distance - disp_intersect) / bData->bNormal[index].normal_scale; + if (depth<0.0f) depth = 0.0f; + } + dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, timescale); + } + + } + + return 1; +} + + +/***************************** Dynamic Paint Step / Baking ******************************/ + +/* +* Calculate current frame neighbouring point distances +* and direction vectors +*/ +static void dynamicPaint_prepareNeighbourData(DynamicPaintSurface *surface) +{ + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + BakeNeighPoint *bNeighs; + PaintAdjData *adj_data = sData->adj_data; + Vec3f *realCoord = bData->realCoord; + int index; + + if (!surface_usesAdjDistance(surface) || !sData->adj_data) return; + + bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets*sizeof(struct BakeNeighPoint),"PaintEffectBake"); + if (!bNeighs) return; + + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + int i; + int numOfNeighs = adj_data->n_num[index]; + + for (i=0; i<numOfNeighs; i++) { + int n_index = adj_data->n_index[index]+i; + int t_index = adj_data->n_target[n_index]; + + /* dir vec */ + sub_v3_v3v3(bNeighs[n_index].dir, realCoord[bData->s_pos[t_index]].v, realCoord[bData->s_pos[index]].v); + /* dist */ + bNeighs[n_index].dist = len_v3(bNeighs[n_index].dir); + /* normalize dir */ + if (bNeighs[n_index].dist) mul_v3_fl(bNeighs[n_index].dir, 1.0f/bNeighs[n_index].dist); + } + } +} + +/* paint effect default movement per frame in global units */ +#define EFF_MOVEMENT_PER_FRAME 0.05f + +/* +* Prepare data required by effects for current frame. +* Returns number of steps required +*/ +static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale) +{ + double average_dist = 0.0f; + double average_force = 0.0f; + float shrink_speed=0.0f, spread_speed=0.0f; + float fastest_effect; + int steps; + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + BakeNeighPoint *bNeighs = bData->bNeighs; + Vec3f *realCoord = bData->realCoord; + int index; + + /* Init force data if required */ + if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { + float vel[3] = {0}; + ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights); + + /* allocate memory for force data (dir vector + strength) */ + *force = MEM_mallocN(sData->total_points*4*sizeof(float), "PaintEffectForces"); + + if (*force) { + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + float forc[3] = {0}; + + /* apply force fields */ + if (effectors) { + EffectedPoint epoint; + pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint); + epoint.vel_to_sec = 1.0f; + pdDoEffectors(effectors, NULL, surface->effector_weights, &epoint, forc, NULL); + } + + /* if global gravity is enabled, add it too */ + if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) + /* also divide by 10 to about match default grav + * with default force strength (1.0) */ + madd_v3_v3fl(forc, scene->physics_settings.gravity, + surface->effector_weights->global_gravity*surface->effector_weights->weight[0] / 10.f); + + /* force strength */ + (*force)[index*4+3] = len_v3(forc); + /* normalize and copy */ + if ((*force)[index*4+3]) mul_v3_fl(forc, 1.0f/(*force)[index*4+3]); + VECCOPY(&((*force)[index*4]), forc); + } + } + pdEndEffectors(&effectors); + } + + /* calculate average values (single thread) */ + for (index = 0; index < sData->total_points; index++) + { + int i; + int numOfNeighs = sData->adj_data->n_num[index]; + + if (*force) + average_force += (*force)[index*4+3]; + + for (i=0; i<numOfNeighs; i++) { + average_dist += bNeighs[sData->adj_data->n_index[index]+i].dist; + } + } + average_force /= sData->total_points; + average_dist /= sData->adj_data->total_targets; + + /* Get number of required steps using averate point distance + * so that just a few ultra close pixels wont up substeps to max */ + + /* adjust number of required substep by fastest active effect */ + if (surface->effect & MOD_DPAINT_EFFECT_DO_SPREAD) + spread_speed = surface->spread_speed; + if (surface->effect & MOD_DPAINT_EFFECT_DO_SHRINK) + shrink_speed = surface->shrink_speed; + + fastest_effect = MAX3(spread_speed, shrink_speed, average_force); + + steps = (int)ceil(EFF_MOVEMENT_PER_FRAME*fastest_effect/average_dist*timescale); + CLAMP(steps, 1, 8); + + //printf("Average distance is %f, avg force %f, num of steps %i\n", average_dist, average_force, steps); + + return steps; +} + +/* +* Processes active effect step. +*/ +static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force, PaintPoint *prevPoint, float timescale) +{ + PaintSurfaceData *sData = surface->data; + BakeNeighPoint *bNeighs = sData->bData->bNeighs; + int index; + + if (!sData->adj_data) return; + /* + * Spread Effect + */ + if (surface->effect & MOD_DPAINT_EFFECT_DO_SPREAD) { + float eff_scale = EFF_MOVEMENT_PER_FRAME*surface->spread_speed*timescale; + + /* Copy current surface to the previous points array to read unmodified values */ + memcpy(prevPoint, sData->type_data, sData->total_points*sizeof(struct PaintPoint)); + + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + int i; + int numOfNeighs = sData->adj_data->n_num[index]; + float totalAlpha = 0.0f; + PaintPoint *pPoint = &((PaintPoint*)sData->type_data)[index]; + + /* + * Only reads values from the surface copy (prevPoint[]), + * so this one is thread safe + */ + + /* Loop through neighbouring points */ + for (i=0; i<numOfNeighs; i++) { + int n_index = sData->adj_data->n_index[index]+i; + float w_factor, alphaAdd = 0.0f; + PaintPoint *ePoint = &prevPoint[sData->adj_data->n_target[n_index]]; + float speed_scale = (bNeighs[n_index].dist<eff_scale) ? 1.0f : eff_scale/bNeighs[n_index].dist; + + totalAlpha += ePoint->e_alpha; + + /* Check if neighbouring point has higher wetness, + * if so, add it's wetness to this point as well*/ + if (ePoint->wetness <= pPoint->wetness) continue; + w_factor = ePoint->wetness/numOfNeighs * (ePoint->wetness - pPoint->wetness) * speed_scale; + if (w_factor <= 0.0f) continue; + + if (ePoint->e_alpha > pPoint->e_alpha) { + alphaAdd = ePoint->e_alpha/numOfNeighs * (ePoint->wetness*ePoint->e_alpha - pPoint->wetness*pPoint->e_alpha) * speed_scale; + } + + /* mix new color */ + mixColors(pPoint->e_color, pPoint->e_alpha, ePoint->e_color, w_factor); + + pPoint->e_alpha += alphaAdd; + pPoint->wetness += w_factor; + + if (pPoint->e_alpha > 1.0f) pPoint->e_alpha = 1.0f; + } + + /* For antialiasing sake, don't let alpha go much higher than average alpha of neighbours */ + if (pPoint->e_alpha > (totalAlpha/numOfNeighs+0.25f)) { + pPoint->e_alpha = (totalAlpha/numOfNeighs+0.25f); + if (pPoint->e_alpha>1.0f) pPoint->e_alpha = 1.0f; + } + } + } + + /* + * Shrink Effect + */ + if (surface->effect & MOD_DPAINT_EFFECT_DO_SHRINK) { + float eff_scale = EFF_MOVEMENT_PER_FRAME*surface->shrink_speed*timescale; + + /* Copy current surface to the previous points array to read unmodified values */ + memcpy(prevPoint, sData->type_data, sData->total_points*sizeof(struct PaintPoint)); + + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + int i; + int numOfNeighs = sData->adj_data->n_num[index]; + float totalAlpha = 0.0f; + PaintPoint *pPoint = &((PaintPoint*)sData->type_data)[index]; + + for (i=0; i<numOfNeighs; i++) { + int n_index = sData->adj_data->n_index[index]+i; + float speed_scale = (bNeighs[n_index].dist<eff_scale) ? 1.0f : eff_scale/bNeighs[n_index].dist; + PaintPoint *ePoint = &prevPoint[sData->adj_data->n_target[n_index]]; + float a_factor, ea_factor, w_factor; + + totalAlpha += ePoint->e_alpha; + + /* Check if neighbouring point has lower alpha, + * if so, decrease this point's alpha as well*/ + if (pPoint->alpha <= 0.0f && pPoint->e_alpha <= 0.0f && pPoint->wetness <= 0.0f) continue; + + /* decrease factor for dry paint alpha */ + a_factor = (1.0f - ePoint->alpha)/numOfNeighs * (pPoint->alpha - ePoint->alpha) * speed_scale; + if (a_factor < 0.0f) a_factor = 0.0f; + /* decrease factor for wet paint alpha */ + ea_factor = (1.0f - ePoint->e_alpha)/8 * (pPoint->e_alpha - ePoint->e_alpha) * speed_scale; + if (ea_factor < 0.0f) ea_factor = 0.0f; + /* decrease factor for paint wetness */ + w_factor = (1.0f - ePoint->wetness)/8 * (pPoint->wetness - ePoint->wetness) * speed_scale; + if (w_factor < 0.0f) w_factor = 0.0f; + + if (a_factor) { + pPoint->alpha -= a_factor; + if (pPoint->alpha < 0.0f) pPoint->alpha = 0.0f; + pPoint->wetness -= a_factor; + + } + else { + pPoint->e_alpha -= ea_factor; + if (pPoint->e_alpha < 0.0f) pPoint->e_alpha = 0.0f; + pPoint->wetness -= w_factor; + if (pPoint->wetness < 0.0f) pPoint->wetness = 0.0f; + } + } + } + } + + /* + * Drip Effect + */ + if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP && force) + { + float eff_scale = EFF_MOVEMENT_PER_FRAME*timescale/10.0f; + for (index = 0; index < sData->total_points; index++) { + int i; + int numOfNeighs = sData->adj_data->n_num[index]; + PaintPoint *pPoint = &((PaintPoint*)sData->type_data)[index]; + + /* to make dripping happen with as little spread as possible, + * only use two closest point dirs "around" the force dir */ + int closest_id[2] = {-1, -1}; + float closest_d[2] = {-1.0f, -1.0f}; + + /* adjust drip speed depending on wetness */ + float w_factor = pPoint->wetness*0.4 - 0.05f; + if (w_factor <= 0) continue; + w_factor *= w_factor; + + /* find closest neigh dir */ + for (i=0; i<numOfNeighs; i++) { + int n_index = sData->adj_data->n_index[index]+i; + float dir_dot = dot_v3v3(bNeighs[n_index].dir, &force[index*4]); + + if (dir_dot>closest_d[0] && dir_dot>0.0f) {closest_d[0]=dir_dot; closest_id[0]=n_index;} + } + + /* find other neigh */ + for (i=0; i<numOfNeighs; i++) { + int n_index = sData->adj_data->n_index[index]+i; + float dir_dot = dot_v3v3(bNeighs[n_index].dir, &force[index*4]); + float closest_dot = dot_v3v3(bNeighs[n_index].dir, bNeighs[closest_id[0]].dir); + + if (n_index == closest_id[0]) continue; + + /* only accept neighbour at "other side" of the first one in relation to force dir + * so make sure angle between this and closest neigh is greater than first angle */ + if (dir_dot>closest_d[1] && closest_dot<closest_d[0] && dir_dot>0.0f) {closest_d[1]=dir_dot; closest_id[1]=n_index;} + } + + /* if two valid neighs found, change closest_d + * values to match final paint factors */ + if (closest_id[1] != -1) { + float neigh_diff = dot_v3v3(bNeighs[closest_id[0]].dir, bNeighs[closest_id[1]].dir); + /* linearize both values */ + neigh_diff *= neigh_diff; + closest_d[0] *= closest_d[0]; + + /* get relation to angle between neighs */ + closest_d[0] -= neigh_diff; + /* and normalize as relative angle within neigh range */ + closest_d[0] /= 1.0f - neigh_diff; + + /* simply set other neigh to cover missing half of this factor */ + closest_d[1] = 1.0f - closest_d[0]; + } + else if (closest_id[0] != -1) { + /* if only one neigh, still linearize to minimize spread */ + closest_d[0] *= closest_d[0]; + } + + + /* Apply movement towards those two points */ + for (i=0; i<2; i++) { + int n_index = closest_id[i]; + if (n_index != -1 && closest_d[i]>0.0f) { + float dir_dot = closest_d[i], dir_factor; + float speed_scale = eff_scale*force[index*4+3]/bNeighs[n_index].dist; + PaintPoint *ePoint = &((PaintPoint*)sData->type_data)[sData->adj_data->n_target[n_index]]; + + /* just skip if angle is too extreme */ + if (dir_dot <= 0.0f) continue; + + dir_factor = dir_dot * speed_scale * w_factor; + if (dir_factor > 1.0f) dir_factor = 1.0f; + + /* mix new color */ + if (dir_factor) mixColors(ePoint->e_color, ePoint->e_alpha, pPoint->e_color, dir_factor); + + ePoint->e_alpha += dir_factor; + ePoint->wetness += dir_factor; + if (ePoint->e_alpha > 1.0f) ePoint->e_alpha = 1.0f; + + /* and decrease paint wetness on current point */ + pPoint->wetness -= dir_factor; + } + } + } + + /* Keep values within acceptable range */ + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) + { + PaintPoint *cPoint = &((PaintPoint*)sData->type_data)[index]; + + if (cPoint->e_alpha > 1.0f) cPoint->e_alpha=1.0f; + if (cPoint->wetness > 2.5f) cPoint->wetness=2.5f; + + if (cPoint->e_alpha < 0.0f) cPoint->e_alpha=0.0f; + if (cPoint->wetness < 0.0f) cPoint->wetness=0.0f; + } + } +} + +#define WAVE_TIME_FAC 0.1 + +void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescale) +{ + PaintSurfaceData *sData = surface->data; + BakeNeighPoint *bNeighs = sData->bData->bNeighs; + int index; + int steps, ss; + float dt, min_dist, damp_factor; + float wave_speed = surface->wave_speed; + double average_dist = 0.0f; + + /* allocate memory */ + PaintWavePoint *prevPoint = MEM_mallocN(sData->total_points*sizeof(PaintWavePoint), "Temp previous points for wave simulation"); + if (!prevPoint) return; + + /* calculate average neigh distance (single thread) */ + for (index = 0; index < sData->total_points; index++) + { + int i; + int numOfNeighs = sData->adj_data->n_num[index]; + + for (i=0; i<numOfNeighs; i++) { + average_dist += bNeighs[sData->adj_data->n_index[index]+i].dist; + } + } + average_dist /= sData->adj_data->total_targets; + + /* determine number of required steps */ + steps = ceil((WAVE_TIME_FAC*timescale*surface->wave_timescale) / (average_dist/wave_speed/3)); + CLAMP(steps, 1, 15); + timescale /= steps; + + /* apply simulation values for final timescale */ + dt = WAVE_TIME_FAC*timescale*surface->wave_timescale; + min_dist = wave_speed*dt*1.5f; + damp_factor = pow((1.0f-surface->wave_damping), 1.0f*timescale); + + for (ss=0; ss<steps; ss++) { + + /* copy previous frame data */ + memcpy(prevPoint, sData->type_data, sData->total_points*sizeof(PaintWavePoint)); + + #pragma omp parallel for schedule(static) + for (index = 0; index < sData->total_points; index++) { + PaintWavePoint *wPoint = &((PaintWavePoint*)sData->type_data)[index]; + int numOfNeighs = sData->adj_data->n_num[index]; + float force = 0.0f, avg_dist = 0.0f, avg_height = 0.0f; + int numOfN = 0, numOfRN = 0; + int i; + + if (wPoint->state) continue; + + /* calculate force from surrounding points */ + for (i=0; i<numOfNeighs; i++) { + int n_index = sData->adj_data->n_index[index]+i; + float dist = bNeighs[n_index].dist; + PaintWavePoint *tPoint = &prevPoint[sData->adj_data->n_target[n_index]]; + + if (!dist || tPoint->state) continue; + if (dist<min_dist) dist=min_dist; + avg_dist += dist; + numOfN++; + + /* count average height for edge points for open borders */ + if (!(sData->adj_data->flags[sData->adj_data->n_target[n_index]] & ADJ_ON_MESH_EDGE)) { + avg_height += tPoint->height; + numOfRN++; + } + + force += (tPoint->height - wPoint->height) / (dist*dist); + } + avg_dist = (numOfN) ? avg_dist/numOfN : 0.0f; + + if (surface->flags & MOD_DPAINT_WAVE_OPEN_BORDERS && + sData->adj_data->flags[index] & ADJ_ON_MESH_EDGE) { + + /* if open borders, apply a fake height to keep waves going on */ + avg_height = (numOfRN) ? avg_height/numOfRN : 0.0f; + + wPoint->height = (dt*wave_speed*avg_height + wPoint->height*avg_dist) / (avg_dist + dt*wave_speed); + } + /* else, do wave eq */ + else { + /* add force towards zero height based on average dist */ + if (avg_dist) + force += (0.0f - wPoint->height) * surface->wave_spring / (avg_dist*avg_dist) / 2.0f; + + /* change point velocity */ + wPoint->velocity += force*dt * wave_speed*wave_speed; + /* damping */ + wPoint->velocity *= damp_factor; + + /* and new height */ + wPoint->height += wPoint->velocity*dt; + } + } + } + + /* reset d_obs and state */ + for (index = 0; index < sData->total_points; index++) { + PaintWavePoint *wPoint = &((PaintWavePoint*)sData->type_data)[index]; + wPoint->state = 0; + } + + MEM_freeN(prevPoint); +} + +#define VALUE_DISSOLVE(VALUE, SPEED, SCALE, LOG) (VALUE) = (LOG) ? (VALUE) * 1.0f - 1.0f/((SPEED)/(SCALE)) : (VALUE) - 1.0f/(SPEED)*(SCALE) + +/* Do dissolve and fading effects */ +static void dynamicPaint_surfacePreStep(DynamicPaintSurface *surface, float timescale) { + PaintSurfaceData *sData = surface->data; + int index; + + #pragma omp parallel for schedule(static) + for (index=0; index<sData->total_points; index++) + { + /* Do drying dissolve effects */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + PaintPoint *pPoint = &((PaintPoint*)sData->type_data)[index]; + /* drying */ + if (pPoint->wetness > 0.0f) { + /* for every drying step blend wet paint to the background */ + if (pPoint->e_alpha) mixColors(pPoint->color, pPoint->alpha, pPoint->e_color, pPoint->e_alpha); + pPoint->state = 1; + + /* only increase alpha if wet paint has higher */ + if (pPoint->e_alpha > pPoint->alpha) pPoint->alpha = pPoint->e_alpha; + + /* now dry it ;o */ + VALUE_DISSOLVE(pPoint->wetness, surface->dry_speed, timescale, (surface->flags & MOD_DPAINT_DRY_LOG)); + } + /* If effect layer is completely dry, make sure it's marked empty */ + if (pPoint->wetness <= 0.0f) { + pPoint->wetness = 0.0f; + pPoint->e_alpha = 0.0f; + pPoint->state = 0; + } + + if (surface->flags & MOD_DPAINT_DISSOLVE) { + + VALUE_DISSOLVE(pPoint->alpha, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); + if (pPoint->alpha < 0.0f) pPoint->alpha = 0.0f; + + VALUE_DISSOLVE(pPoint->e_alpha, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); + if (pPoint->e_alpha < 0.0f) pPoint->e_alpha = 0.0f; + } + } + /* dissolve for float types */ + else if (surface->flags & MOD_DPAINT_DISSOLVE && + (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)) { + + float *point = &((float*)sData->type_data)[index]; + /* log or linear */ + VALUE_DISSOLVE(*point, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); + if (*point < 0.0f) *point = 0.0f; + } + } +} + +static int dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob) +{ + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + DerivedMesh *dm = surface->canvas->dm; + MVert *mvert = dm->getVertArray(dm); + + int numOfVerts = dm->getNumVerts(dm); + int i; + int ret = 0; + + if (!bData->prev_verts) return 1; + + /* matrix */ + for (i=0; i<4; i++) { + int j; + for (j=0; j<4; j++) + if (bData->prev_obmat[i][j] != ob->obmat[i][j]) return 1; + } + + /* vertices */ + #pragma omp parallel for schedule(static) + for (i=0; i<numOfVerts; i++) { + int j; + for (j=0; j<3; j++) + if (bData->prev_verts[i].co[j] != mvert[i].co[j]) { + ret = 1; + break; + } + } + + return 0; +} + +/* Prepare for surface step by creating PaintBakeNormal data */ +static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, Object *ob) +{ + PaintSurfaceData *sData = surface->data; + PaintAdjData *adj_data = sData->adj_data; + PaintBakeData *bData = sData->bData; + DerivedMesh *dm = surface->canvas->dm; + int index; + + int canvasNumOfVerts = dm->getNumVerts(dm); + MVert *mvert = dm->getVertArray(dm); + Vec3f *canvas_verts; + + /* if previous data exists and mesh hasn't moved, no need to recalc */ + if (bData && !dynamicPaint_surfaceHasMoved(surface, ob)) return 1; + + canvas_verts = (struct Vec3f *) MEM_mallocN(canvasNumOfVerts*sizeof(struct Vec3f), "Dynamic Paint transformed canvas verts"); + if (!canvas_verts) return 0; + + /* alloc memory if required */ + if (!bData) { + sData->bData = bData = (struct PaintBakeData *) MEM_callocN(sizeof(struct PaintBakeData), "Dynamic Paint bake data"); + if (!bData) { + if (canvas_verts) MEM_freeN(canvas_verts); + return 0; + } + + /* Init bdata */ + bData->bNormal = (struct PaintBakeNormal *) MEM_mallocN(sData->total_points*sizeof(struct PaintBakeNormal), "Dynamic Paint step data"); + bData->s_pos = MEM_mallocN(sData->total_points*sizeof(unsigned int), "Dynamic Paint bData s_pos"); + bData->s_num = MEM_mallocN(sData->total_points*sizeof(unsigned int), "Dynamic Paint bData s_num"); + bData->realCoord = (struct Vec3f *) MEM_mallocN(surface_totalSamples(surface)*sizeof(Vec3f), "Dynamic Paint point coords"); + + bData->prev_verts = MEM_mallocN(canvasNumOfVerts*sizeof(MVert), "Dynamic Paint bData prev_verts"); + + if (!bData->bNormal || !bData->s_pos || !bData->s_num || !bData->realCoord || !canvas_verts) { + if (bData->bNormal) MEM_freeN(bData->bNormal); + if (bData->s_pos) MEM_freeN(bData->s_pos); + if (bData->s_num) MEM_freeN(bData->s_num); + if (bData->realCoord) MEM_freeN(bData->realCoord); + if (canvas_verts) MEM_freeN(canvas_verts); + + return printError(surface->canvas, "Not enough free memory."); + } + } + + /* + * Make a transformed copy of canvas derived mesh vertices to avoid recalculation. + */ + #pragma omp parallel for schedule(static) + for (index=0; index<canvasNumOfVerts; index++) { + /* Multiply coordinates by canvas transformation matrix */ + VECCOPY(canvas_verts[index].v, mvert[index].co); + mul_m4_v3(ob->obmat, canvas_verts[index].v); + } + + /* + * Prepare each surface point for a new step + */ + #pragma omp parallel for schedule(static) + for (index=0; index<sData->total_points; index++) + { + /* + * Calculate current 3D-position of each surface pixel + */ + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { + float n1[3], n2[3], n3[3]; + ImgSeqFormatData *f_data = (ImgSeqFormatData*)sData->format_data; + PaintUVPoint *tPoint = &((PaintUVPoint*)f_data->uv_p)[index]; + int ss; + + bData->s_num[index] = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + bData->s_pos[index] = index * bData->s_num[index]; + + /* per sample coordinates */ + for (ss=0; ss<bData->s_num[index]; ss++) { + interp_v3_v3v3v3( bData->realCoord[bData->s_pos[index]+ss].v, + canvas_verts[tPoint->v1].v, + canvas_verts[tPoint->v2].v, + canvas_verts[tPoint->v3].v, f_data->barycentricWeights[index*bData->s_num[index]+ss].v); + } + + /* Calculate current pixel surface normal */ + normal_short_to_float_v3(n1, mvert[tPoint->v1].no); + normal_short_to_float_v3(n2, mvert[tPoint->v2].no); + normal_short_to_float_v3(n3, mvert[tPoint->v3].no); + + interp_v3_v3v3v3( bData->bNormal[index].invNorm, + n1, n2, n3, f_data->barycentricWeights[index*bData->s_num[index]].v); + mul_mat3_m4_v3(ob->obmat, bData->bNormal[index].invNorm); + normalize_v3(bData->bNormal[index].invNorm); + negate_v3(bData->bNormal[index].invNorm); + } + else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + /* In case of verted data */ + int ss; + + if (surface->flags & MOD_DPAINT_ANTIALIAS && adj_data) { + bData->s_num[index] = adj_data->n_num[index]+1; + bData->s_pos[index] = adj_data->n_index[index]+index; + } + else { + bData->s_num[index] = 1; + bData->s_pos[index] = index; + } + + /* per sample coordinates */ + for (ss=0; ss<bData->s_num[index]; ss++) { + /* first sample is always point center */ + VECCOPY(bData->realCoord[bData->s_pos[index]+ss].v, canvas_verts[index].v); + if (ss > 0) { + int t_index = adj_data->n_index[index]+(ss-1); + /* get vertex position at 1/3 of each neigh edge */ + mul_v3_fl(bData->realCoord[bData->s_pos[index]+ss].v, 2.0f/3.0f); + madd_v3_v3fl(bData->realCoord[bData->s_pos[index]+ss].v, canvas_verts[adj_data->n_target[t_index]].v, 1.0f/3.0f); + } + } + + /* normal */ + normal_short_to_float_v3(bData->bNormal[index].invNorm, mvert[index].no); + mul_mat3_m4_v3(ob->obmat, bData->bNormal[index].invNorm); + normalize_v3(bData->bNormal[index].invNorm); + negate_v3(bData->bNormal[index].invNorm); + } + + /* Prepare special data for surface types */ + if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + float temp_nor[3]; + if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + normal_short_to_float_v3(temp_nor, mvert[index].no); + normalize_v3(temp_nor); + } + else { + temp_nor[0]=0.0f; + temp_nor[1]=0.0f; + temp_nor[2]=1.0f; + } + + mul_v3_v3 (temp_nor, ob->size); + bData->bNormal[index].normal_scale = len_v3(temp_nor); + } + } + + MEM_freeN(canvas_verts); + + /* generate surface space partitioning grid */ + surfaceGenerateGrid(surface); + + /* calculate current frame neighbouring point distances and global dirs */ + dynamicPaint_prepareNeighbourData(surface); + + /* Copy current frame position to check against in next frame */ + copy_m4_m4(bData->prev_obmat, ob->obmat); + memcpy(bData->prev_verts, mvert, canvasNumOfVerts*sizeof(MVert)); + + return 1; +} + +static void subframe_updateObject(Scene *scene, Object *ob, int update_parents, float frame) +{ + int oflags; + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)modifiers_findByType(ob, eModifierType_DynamicPaint); + + /* if other is dynamic paint canvas, dont update */ + if (pmd && pmd->canvas) + return; + + /* if object has parent, update it too */ + if (update_parents && ob->parent) subframe_updateObject(scene, ob->parent, 0, frame); + if (update_parents && ob->track) subframe_updateObject(scene, ob->track, 0, frame); + + /* for curve */ + if(ob->type==OB_CURVE) { + Curve *cu= ob->data; + BKE_animsys_evaluate_animdata(&cu->id, cu->adt, frame, ADT_RECALC_ANIM); + } + + /* backup object flags */ + oflags = ob->recalc; + + ob->recalc |= OB_RECALC_ALL; + ob->recalc |= OB_RECALC_DATA; + BKE_animsys_evaluate_animdata(&ob->id, ob->adt, frame, ADT_RECALC_ANIM); + object_handle_update(scene, ob); + + /* restore flags */ + ob->recalc = oflags; +} + +static void scene_setSubframe(Scene *scene, float subframe) +{ + /* dynamic paint subframes must be done on previous frame */ + scene->r.cfra -= 1; + scene->r.subframe = subframe; +} + +/* +* Do Dynamic Paint Step. Paints scene brush objects of current state/frame to canvas. +*/ +static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *surface, float timescale, float subframe) +{ + PaintSurfaceData *sData = surface->data; + PaintBakeData *bData = sData->bData; + DynamicPaintCanvasSettings *canvas = surface->canvas; + int ret = 1; + + if (!sData || sData->total_points < 1) return 0; + + dynamicPaint_surfacePreStep(surface, timescale); + /* + * Loop through surface's target paint objects and do painting + */ + { + Base *base = NULL; + GroupObject *go = NULL; + + Object *brushObj = NULL; + ModifierData *md = NULL; + + /* backup current scene frame */ + int scene_frame = scene->r.cfra; + float scene_subframe = scene->r.subframe; + + /* either from group or from all objects */ + if(surface->brush_group) + go = surface->brush_group->gobject.first; + else + base = scene->base.first; + + while (base || go) + { + brushObj = NULL; + + /* select object */ + if(surface->brush_group) { + if(go->ob) brushObj = go->ob; + } + else + brushObj = base->object; + + if(!brushObj) + { + /* skip item */ + if(surface->brush_group) go = go->next; + else base= base->next; + continue; + } + + /* next item */ + if(surface->brush_group) + go = go->next; + else + base= base->next; + + md = modifiers_findByType(brushObj, 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->brush) + { + DynamicPaintBrushSettings *brush = pmd2->brush; + + /* update object position on this subframe */ + if (subframe) { + scene_setSubframe(scene, subframe); + subframe_updateObject(scene, brushObj, 1, BKE_curframe(scene)); + } + + /* If using material color, update anim data to current (sub)frame */ + if (brush->flags & MOD_DPAINT_USE_MATERIAL) + dynamicPaint_updateObjectMaterials(brushObj, brush->mat, scene); + + /* Check if painter has a particle system selected + * -> if so, do particle painting */ + if (brush->collision == MOD_DPAINT_COL_PSYS) + { + if (brush && brush->psys && brush->psys->part && brush->psys->part->type==PART_EMITTER) + if (psys_check_enabled(brushObj, brush->psys)) { + + /* Paint a particle system */ + BKE_animsys_evaluate_animdata(&brush->psys->part->id, brush->psys->part->adt, BKE_curframe(scene), ADT_RECALC_ANIM); + dynamicPaint_paintParticles(surface, brush->psys, brush, ob, timescale); + } + } + else if (brush->collision == MOD_DPAINT_COL_POINT && brushObj != ob) { + dynamicPaint_paintSinglePoint(surface, brushObj->loc, brush, ob, timescale); + } + else if (brushObj != ob){ + /* Paint a mesh */ + dynamicPaint_paintMesh(surface, brush, ob, brushObj, timescale); + } + + /* return object to it's original state */ + if (subframe) { + scene->r.cfra = scene_frame; + scene->r.subframe = scene_subframe; + subframe_updateObject(scene, brushObj, 1, BKE_curframe(scene)); + dynamicPaint_updateObjectMaterials(brushObj, brush->mat, scene); + } + } /* end of collision check (Is valid paint modifier) */ + } + } + } + + /* surfaces operations that use adjacency data */ + if (sData->adj_data && bData->bNeighs) + { + /* wave type surface simulation step */ + if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + dynamicPaint_doWaveStep(surface, timescale); + } + + /* paint movement effects */ + if (surface->effect && surface->type == MOD_DPAINT_SURFACE_T_PAINT) + { + int steps = 1, s; + PaintPoint *prevPoint; + float *force = NULL; + + /* Allocate memory for surface previous points to read unchanged values from */ + prevPoint = MEM_mallocN(sData->total_points*sizeof(struct PaintPoint), "PaintSurfaceDataCopy"); + if (!prevPoint) { + if (prevPoint) MEM_freeN(prevPoint); + return printError(canvas, "Not enough free memory."); + } + + /* Prepare effects and get number of required steps */ + steps = dynamicPaint_prepareEffectStep(surface, scene, ob, &force, timescale); + + /* + * Do Effects steps + */ + for (s = 0; s < steps; s++) + { + dynamicPaint_doEffectStep(surface, force, prevPoint, timescale/(float)steps); + } + + /* Free temporary effect data */ + if (prevPoint) MEM_freeN(prevPoint); + if (force) MEM_freeN(force); + } + } + + return ret; +} + +/* +* Calculate a single frame for canvas point cache +*/ +static int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Scene *scene, Object *cObject, int frame) +{ + float timescale = 1.0f; + int ret; + + /* update bake data */ + dynamicPaint_generateBakeData(surface, cObject); + + /* dont do substeps for first frame */ + if (surface->substeps && (frame != surface->start_frame)) { + int st; + timescale = 1.0f / (surface->substeps+1); + + for (st = 1; st <= surface->substeps; st++) + { + float subframe = ((float) st) / (surface->substeps+1); + + if (!dynamicPaint_doStep(scene, cObject, surface, timescale, subframe)) return 0; + } + } + + ret = dynamicPaint_doStep(scene, cObject, surface, timescale, 0.0f); + + return ret; +} + +/***************************** Image Sequence Baking ******************************/ + +/* +* Do actual bake operation. Loops through to-be-baked frames. +* Returns 0 on failture. +*/ +static int dynamicPaint_bakeImageSequence(bContext *C, DynamicPaintSurface *surface, Object *cObject) +{ + DynamicPaintCanvasSettings *canvas = surface->canvas; + Scene *scene= CTX_data_scene(C); + wmWindow *win = CTX_wm_window(C); + int frame = 1; + int frames; + + frames = surface->end_frame - surface->start_frame + 1; + if (frames <= 0) {return printError(canvas, "No frames to bake.");} + + /* + * Set frame to start point (also inits modifier data) + */ + frame = surface->start_frame; + scene->r.cfra = (int)frame; + ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1); + + /* Init surface */ + if (!dynamicPaint_createUVSurface(surface)) return 0; + + /* + * Loop through selected frames + */ + for (frame=surface->start_frame; frame<=surface->end_frame; frame++) + { + float progress = (frame - surface->start_frame) / (float)frames * 100; + surface->current_frame = frame; + + /* 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); + + /* calculate a frame */ + scene->r.cfra = (int)frame; + ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1); + if (!dynamicPaint_calculateFrame(surface, scene, cObject, frame)) return 0; + + /* + * Save output images + */ + { + char filename[250]; + char pad[4]; + char dir_slash[2]; + /* OpenEXR or PNG */ + short format = (surface->image_fileformat & MOD_DPAINT_IMGFORMAT_OPENEXR) ? DPOUTPUT_OPENEXR : DPOUTPUT_PNG; + + /* 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'; + + /* make sure directory path is valid to append filename */ + if (surface->image_output_path[strlen(surface->image_output_path)-1] != 47 && + surface->image_output_path[strlen(surface->image_output_path)-1] != 92) + strcpy(dir_slash,"/"); + else + dir_slash[0] = '\0'; + + + /* color map */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + if (surface->flags & MOD_DPAINT_OUT1) { + sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name, pad, (int)frame); + dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_PAINT); + } + if (surface->flags & MOD_DPAINT_OUT2) { + sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name2, pad, (int)frame); + dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_WET); + } + } + + /* displacement map */ + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { + sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name, pad, (int)frame); + dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_DISPLACE); + } + + /* waves */ + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { + sprintf(filename, "%s%s%s%s%i", surface->image_output_path, dir_slash, surface->output_name, pad, (int)frame); + dynamicPaint_outputImage(surface, filename, format, DPOUTPUT_WAVES); + } + } + } + return 1; +} + + +/* +* An operator call to start baking dynamic paint image sequences for active object +*/ +static int dynamicPaint_initBake(bContext *C, wmOperator *op) +{ + DynamicPaintModifierData *pmd = NULL; + Object *cObject = CTX_data_pointer_get_type(C, "object", &RNA_Object).data; + int status = 0; + double timer = PIL_check_seconds_timer(); + DynamicPaintSurface *surface; + + /* + * Get modifier data + */ + pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint); + if (!pmd) { + BKE_report(op->reports, RPT_ERROR, "Bake Failed: No Dynamic Paint modifier found."); + return 0; + } + + /* Make sure we're dealing with a canvas */ + if (!pmd->canvas) { + BKE_report(op->reports, RPT_ERROR, "Bake Failed: Invalid Canvas."); + return 0; + } + surface = get_activeSurface(pmd->canvas); + + /* Set state to baking and init surface */ + pmd->canvas->error[0] = '\0'; + pmd->canvas->flags |= MOD_DPAINT_BAKING; + G.afbreek= 0; /* reset blender_test_break*/ + + /* Bake Dynamic Paint */ + status = dynamicPaint_bakeImageSequence(C, surface, cObject); + /* Clear bake */ + pmd->canvas->flags &= ~MOD_DPAINT_BAKING; + WM_cursor_restore(CTX_wm_window(C)); + dynamicPaint_freeSurfaceData(surface); + + /* 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(!dynamicPaint_initBake(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; +} + +static int surface_slot_add_exec(bContext *C, wmOperator *op) +{ + DynamicPaintModifierData *pmd = NULL; + Object *cObject = CTX_data_pointer_get_type(C, "object", &RNA_Object).data; + DynamicPaintSurface *surface; + + /* Make sure we're dealing with a canvas */ + pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint); + if (!pmd) return OPERATOR_CANCELLED; + if (!pmd->canvas) return OPERATOR_CANCELLED; + + surface = dynamicPaint_createNewSurface(pmd->canvas, CTX_data_scene(C)); + + if (!surface) return OPERATOR_CANCELLED; + + /* set preview for this surface only and set active */ + pmd->canvas->active_sur = 0; + for(surface=surface->prev; surface; surface=surface->prev) { + surface->flags &= ~MOD_DPAINT_PREVIEW; + pmd->canvas->active_sur++; + } + + return OPERATOR_FINISHED; +} + +/* add surface slot */ +void DPAINT_OT_surface_slot_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Surface Slot"; + ot->idname= "DPAINT_OT_surface_slot_add"; + ot->description="Add a new Dynamic Paint surface slot"; + + /* api callbacks */ + ot->exec= surface_slot_add_exec; + ot->poll= ED_operator_object_active_editable; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int surface_slot_remove_exec(bContext *C, wmOperator *op) +{ + DynamicPaintModifierData *pmd = NULL; + Object *cObject = CTX_data_pointer_get_type(C, "object", &RNA_Object).data; + DynamicPaintSurface *surface; + int id=0; + + /* Make sure we're dealing with a canvas */ + pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint); + if (!pmd) return OPERATOR_CANCELLED; + if (!pmd->canvas) return OPERATOR_CANCELLED; + + surface = pmd->canvas->surfaces.first; + + /* find active surface and remove it */ + for(; surface; surface=surface->next) { + if(id == pmd->canvas->active_sur) { + pmd->canvas->active_sur -= 1; + dynamicPaint_freeSurface(surface); + break; + } + id++; + } + + dynamicPaint_resetPreview(pmd->canvas); + DAG_id_tag_update(&cObject->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, cObject); + + return OPERATOR_FINISHED; +} + +/* remove surface slot */ +void DPAINT_OT_surface_slot_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Remove Surface Slot"; + ot->idname= "DPAINT_OT_surface_slot_remove"; + ot->description="Remove the selected surface slot"; + + /* api callbacks */ + ot->exec= surface_slot_remove_exec; + ot->poll= ED_operator_object_active_editable; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int type_toggle_exec(bContext *C, wmOperator *op) { + + Object *cObject = CTX_data_pointer_get_type(C, "object", &RNA_Object).data; + Scene *scene = CTX_data_scene(C); + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)modifiers_findByType(cObject, eModifierType_DynamicPaint); + int type= RNA_enum_get(op->ptr, "type"); + + if (!pmd) return OPERATOR_CANCELLED; + + /* if type is already enabled, toggle it off */ + if (type == MOD_DYNAMICPAINT_TYPE_CANVAS && pmd->canvas) { + dynamicPaint_freeCanvas(pmd); + } + else if (type == MOD_DYNAMICPAINT_TYPE_BRUSH && pmd->brush) { + dynamicPaint_freeBrush(pmd); + } + /* else create a new type */ + else { + if (!dynamicPaint_createType(pmd, type, scene)) + return OPERATOR_CANCELLED; + } + + /* update dependancy */ + DAG_id_tag_update(&cObject->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, cObject); + DAG_scene_sort(CTX_data_main(C), scene); + + return OPERATOR_FINISHED; +} + +void DPAINT_OT_type_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name= "Toggle Type Active"; + ot->idname= "DPAINT_OT_type_toggle"; + ot->description = "Toggles whether given type is active or not"; + + /* api callbacks */ + ot->exec= type_toggle_exec; + ot->poll= ED_operator_object_active_editable; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + prop= RNA_def_enum(ot->srna, "type", prop_dynamicpaint_type_items, MOD_DYNAMICPAINT_TYPE_CANVAS, "Type", ""); + ot->prop= prop; +} + diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 5995b895061..51971f8d72d 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" @@ -3453,6 +3454,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->brush && pmd->brush->psys) + if(pmd->brush->psys == psys) + pmd->brush->psys = NULL; + } + /* clear modifier */ psmd= psys_get_modifier(ob, psys); BLI_remlink(&ob->modifiers, psmd); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index b8f4b2d302f..e347e3896b0 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -40,6 +40,7 @@ #include "DNA_ID.h" #include "DNA_cloth_types.h" +#include "DNA_dynamicpaint_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" @@ -60,6 +61,7 @@ #include "BKE_blender.h" #include "BKE_cloth.h" #include "BKE_depsgraph.h" +#include "BKE_dynamicpaint.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" @@ -603,7 +605,7 @@ static int ptcache_smoke_write(PTCacheFile *pf, void *smoke_v) return ret; } -static void ptcache_smoke_read(PTCacheFile *pf, void *smoke_v) +static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v) { SmokeModifierData *smd= (SmokeModifierData *)smoke_v; SmokeDomainSettings *sds = smd->domain; @@ -652,6 +654,8 @@ static void ptcache_smoke_read(PTCacheFile *pf, void *smoke_v) ptcache_file_compressed_read(pf, (unsigned char*)tcw, out_len); } } + + return 1; } #else // WITH_SMOKE static int ptcache_smoke_totpoint(void *UNUSED(smoke_v), int UNUSED(cfra)) { return 0; }; @@ -659,6 +663,84 @@ static void ptcache_smoke_read(PTCacheFile *UNUSED(pf), void *UNUSED(smoke_v)) { static int ptcache_smoke_write(PTCacheFile *UNUSED(pf), void *UNUSED(smoke_v)) { return 0; } #endif // WITH_SMOKE +static int ptcache_dynamicpaint_totpoint(void *sd, int cfra) +{ + DynamicPaintSurface *surface = (DynamicPaintSurface*)sd; + + if (!surface->data) return 0; + else return surface->data->total_points; +} + +#define DP_CACHE_VERSION "1.01" + +static int ptcache_dynamicpaint_write(PTCacheFile *pf, void *dp_v) +{ + DynamicPaintSurface *surface = (DynamicPaintSurface*)dp_v; + int cache_compress = 1; + + /* version header */ + ptcache_file_write(pf, DP_CACHE_VERSION, 1, sizeof(char)*4); + + if(surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->data) { + unsigned int total_points=surface->data->total_points; + unsigned int in_len; + unsigned char *out; + + /* cache type */ + ptcache_file_write(pf, &surface->type, 1, sizeof(int)); + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) + in_len = sizeof(PaintPoint)*total_points; + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) + in_len = sizeof(float)*total_points; + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) + in_len = sizeof(PaintWavePoint)*total_points; + else return 0; + + out = (unsigned char *)MEM_callocN(LZO_OUT_LEN(in_len), "pointcache_lzo_buffer"); + + ptcache_file_compressed_write(pf, (unsigned char *)surface->data->type_data, in_len, out, cache_compress); + MEM_freeN(out); + + } + return 1; +} +static int ptcache_dynamicpaint_read(PTCacheFile *pf, void *dp_v) +{ + DynamicPaintSurface *surface = (DynamicPaintSurface*)dp_v; + char version[4]; + + /* version header */ + ptcache_file_read(pf, version, 1, sizeof(char)*4); + if (strncmp(version, DP_CACHE_VERSION,4)) {printf("Dynamic Paint: Invalid cache version: %s!\n",version); return 0;} + + if(surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->data) { + unsigned int data_len; + int surface_type; + + /* cache type */ + ptcache_file_read(pf, &surface_type, 1, sizeof(int)); + + if (surface_type != surface->type) + return 0; + + /* read surface data */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) + data_len = sizeof(PaintPoint); + else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || + surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) + data_len = sizeof(float); + else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) + data_len = sizeof(PaintWavePoint); + else return 0; + + ptcache_file_compressed_read(pf, (unsigned char*)surface->data->type_data, data_len*surface->data->total_points); + + } + return 1; +} + /* Creating ID's */ void BKE_ptcache_id_from_softbody(PTCacheID *pid, Object *ob, SoftBody *sb) { @@ -811,6 +893,40 @@ void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct SmokeMo if(sds->wt) pid->data_types |= (1<<BPHYS_DATA_SMOKE_HIGH); } + +void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, Object *ob, DynamicPaintSurface *surface) +{ + + memset(pid, 0, sizeof(PTCacheID)); + + pid->ob= ob; + pid->calldata= surface; + pid->type= PTCACHE_TYPE_DYNAMICPAINT; + pid->cache= surface->pointcache; + pid->cache_ptr= &surface->pointcache; + pid->ptcaches= &surface->ptcaches; + pid->totpoint= pid->totwrite= ptcache_dynamicpaint_totpoint; + + pid->write_point = NULL; + pid->read_point = NULL; + pid->interpolate_point = NULL; + + pid->write_stream = ptcache_dynamicpaint_write; + pid->read_stream = ptcache_dynamicpaint_read; + + pid->write_extra_data = NULL; + pid->read_extra_data = NULL; + pid->interpolate_extra_data = NULL; + + pid->write_header = ptcache_basic_header_write; + pid->read_header = ptcache_basic_header_read; + + pid->data_types= BPHYS_DATA_DYNAMICPAINT; + pid->info_types= 0; + + pid->stack_index = pid->cache->index; +} + void BKE_ptcache_ids_from_object(ListBase *lb, Object *ob, Scene *scene, int duplis) { PTCacheID *pid; @@ -851,7 +967,7 @@ void BKE_ptcache_ids_from_object(ListBase *lb, Object *ob, Scene *scene, int dup BKE_ptcache_id_from_cloth(pid, ob, (ClothModifierData*)md); BLI_addtail(lb, pid); } - if(md->type == eModifierType_Smoke) { + else if(md->type == eModifierType_Smoke) { SmokeModifierData *smd = (SmokeModifierData *)md; if(smd->type & MOD_SMOKE_TYPE_DOMAIN) { @@ -860,6 +976,19 @@ void BKE_ptcache_ids_from_object(ListBase *lb, Object *ob, Scene *scene, int dup BLI_addtail(lb, pid); } } + else if(md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + if(pmd->canvas) + { + DynamicPaintSurface *surface = pmd->canvas->surfaces.first; + + for (; surface; surface=surface->next) { + pid= MEM_callocN(sizeof(PTCacheID), "PTCacheID"); + BKE_ptcache_id_from_dynamicpaint(pid, ob, surface); + BLI_addtail(lb, pid); + } + } + } } if(scene && (duplis-- > 0) && (ob->transflag & OB_DUPLI)) { @@ -1592,7 +1721,8 @@ static int ptcache_read_stream(PTCacheID *pid, int cfra) ptcache_file_pointers_init(pf); // we have stream reading here - pid->read_stream(pf, pid->calldata); + if (!pid->read_stream(pf, pid->calldata)) + error = 1; } ptcache_file_close(pf); @@ -1728,15 +1858,21 @@ int BKE_ptcache_read(PTCacheID *pid, float cfra) return 0; if(cfra1) { - if(pid->read_stream) - ptcache_read_stream(pid, cfra1); + + if(pid->read_stream) { + if (!ptcache_read_stream(pid, cfra1)) + return 0; + } else if(pid->read_point) ptcache_read(pid, cfra1); } if(cfra2) { - if(pid->read_stream) - ptcache_read_stream(pid, cfra2); + + if(pid->read_stream) { + if (!ptcache_read_stream(pid, cfra2)) + return 0; + } else if(pid->read_point) { if(cfra1 && cfra2 && pid->interpolate_point) ptcache_interpolate(pid, cfra, cfra1, cfra2); @@ -2290,6 +2426,8 @@ int BKE_ptcache_id_reset(Scene *scene, PTCacheID *pid, int mode) smokeModifier_reset(pid->calldata); else if(pid->type == PTCACHE_TYPE_SMOKE_HIGHRES) smokeModifier_reset_turbulence(pid->calldata); + else if(pid->type == PTCACHE_TYPE_DYNAMICPAINT) + dynamicPaint_clearSurface((DynamicPaintSurface*)pid->calldata); } if(clear) BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0); @@ -2346,6 +2484,18 @@ int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode) reset |= BKE_ptcache_id_reset(scene, &pid, mode); } } + if(md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + if(pmd->canvas) + { + DynamicPaintSurface *surface = pmd->canvas->surfaces.first; + + for (; surface; surface=surface->next) { + BKE_ptcache_id_from_dynamicpaint(&pid, ob, surface); + reset |= BKE_ptcache_id_reset(scene, &pid, mode); + } + } + } } if (ob->type == OB_ARMATURE) diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 67d7e7bffd6..f66f7ca023e 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -1824,11 +1824,14 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u glEnable(GL_POLYGON_STIPPLE); glPolygonStipple(stipple_quarttone); } + + /* dont set shading mode to flat because + * normals are used to change shading */ + glShadeModel(GL_SMOOTH); for (S=0; S<numVerts; S++) { DMGridData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S); if (drawSmooth) { - glShadeModel(GL_SMOOTH); for (y=0; y<gridFaces; y++) { DMGridData *a, *b; glBegin(GL_QUAD_STRIP); @@ -1863,7 +1866,6 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u glEnd(); } } else { - glShadeModel(GL_FLAT); glBegin(GL_QUADS); for (y=0; y<gridFaces; y++) { for (x=0; x<gridFaces; x++) { |