diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2016-05-31 10:58:08 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2016-05-31 10:58:08 +0300 |
commit | 7282c6592ad532ce0655deb1149b22bcee9df9e7 (patch) | |
tree | 1bdb4bb7cb988b2772612c2d0537e003838e9ea7 /source/blender | |
parent | 834680596398e7a985987d719152f8480bee7fb8 (diff) | |
parent | 2c9f6714a76399b14e903224dc416adde4ceb7a3 (diff) |
Merge branch 'id-remap' into asset-experiments
Diffstat (limited to 'source/blender')
102 files changed, 7806 insertions, 4722 deletions
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 423b8fade39..bb5cc9cb067 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2003,15 +2003,10 @@ static void mesh_calc_modifiers( DM_add_edge_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); DM_add_poly_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); -#pragma omp parallel sections if (dm->numVertData + dm->numEdgeData + dm->numPolyData >= BKE_MESH_OMP_LIMIT) - { -#pragma omp section - { range_vn_i(DM_get_vert_data_layer(dm, CD_ORIGINDEX), dm->numVertData, 0); } -#pragma omp section - { range_vn_i(DM_get_edge_data_layer(dm, CD_ORIGINDEX), dm->numEdgeData, 0); } -#pragma omp section - { range_vn_i(DM_get_poly_data_layer(dm, CD_ORIGINDEX), dm->numPolyData, 0); } - } + /* Not worth parallelizing this, gives less than 0.1% overall speedup in best of best cases... */ + range_vn_i(DM_get_vert_data_layer(dm, CD_ORIGINDEX), dm->numVertData, 0); + range_vn_i(DM_get_edge_data_layer(dm, CD_ORIGINDEX), dm->numEdgeData, 0); + range_vn_i(DM_get_poly_data_layer(dm, CD_ORIGINDEX), dm->numPolyData, 0); } } @@ -3269,17 +3264,18 @@ void DM_calc_loop_tangents_step_0( bool *rcalc_act, bool *rcalc_ren, int *ract_uv_n, int *rren_uv_n, char *ract_uv_name, char *rren_uv_name, char *rtangent_mask) { /* Active uv in viewport */ + int layer_index = CustomData_get_layer_index(loopData, CD_MLOOPUV); *ract_uv_n = CustomData_get_active_layer(loopData, CD_MLOOPUV); ract_uv_name[0] = 0; if (*ract_uv_n != -1) { - strcpy(ract_uv_name, loopData->layers[*ract_uv_n].name); + strcpy(ract_uv_name, loopData->layers[*ract_uv_n + layer_index].name); } /* Active tangent in render */ *rren_uv_n = CustomData_get_render_layer(loopData, CD_MLOOPUV); rren_uv_name[0] = 0; if (*rren_uv_n != -1) { - strcpy(rren_uv_name, loopData->layers[*rren_uv_n].name); + strcpy(rren_uv_name, loopData->layers[*rren_uv_n + layer_index].name); } /* If active tangent not in tangent_names we take it into account */ diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index bac59c8c62d..c1f1f0128f5 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -43,6 +43,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BKE_colortools.h" @@ -53,10 +54,6 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" -#ifdef _OPENMP -# include <omp.h> -#endif - /* ********************************* color curve ********************* */ /* ***************** operations on full struct ************* */ @@ -1089,31 +1086,170 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorM } /* if view_settings, it also applies this to byte buffers */ +typedef struct ScopesUpdateData { + Scopes *scopes; + const ImBuf *ibuf; + struct ColormanageProcessor *cm_processor; + const unsigned char *display_buffer; + const int ycc_mode; + + unsigned int *bin_lum, *bin_r, *bin_g, *bin_b, *bin_a; +} ScopesUpdateData; + +typedef struct ScopesUpdateDataChunk { + unsigned int bin_lum[256]; + unsigned int bin_r[256]; + unsigned int bin_g[256]; + unsigned int bin_b[256]; + unsigned int bin_a[256]; + float min[3], max[3]; +} ScopesUpdateDataChunk; + +static void scopes_update_cb(void *userdata, void *userdata_chunk, const int y, const int UNUSED(threadid)) +{ + const ScopesUpdateData *data = userdata; + + Scopes *scopes = data->scopes; + const ImBuf *ibuf = data->ibuf; + struct ColormanageProcessor *cm_processor = data->cm_processor; + const unsigned char *display_buffer = data->display_buffer; + const int ycc_mode = data->ycc_mode; + + ScopesUpdateDataChunk *data_chunk = userdata_chunk; + unsigned int *bin_lum = data_chunk->bin_lum; + unsigned int *bin_r = data_chunk->bin_r; + unsigned int *bin_g = data_chunk->bin_g; + unsigned int *bin_b = data_chunk->bin_b; + unsigned int *bin_a = data_chunk->bin_a; + float *min = data_chunk->min; + float *max = data_chunk->max; + + const float *rf = NULL; + const unsigned char *rc = NULL; + const int rows_per_sample_line = ibuf->y / scopes->sample_lines; + const int savedlines = y / rows_per_sample_line; + const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; + const bool is_float = (ibuf->rect_float != NULL); + + if (is_float) + rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; + else { + rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; + } + + for (int x = 0; x < ibuf->x; x++) { + float rgba[4], ycc[3], luma; + + if (is_float) { + switch (ibuf->channels) { + case 4: + copy_v4_v4(rgba, rf); + IMB_colormanagement_processor_apply_v4(cm_processor, rgba); + break; + case 3: + copy_v3_v3(rgba, rf); + IMB_colormanagement_processor_apply_v3(cm_processor, rgba); + rgba[3] = 1.0f; + break; + case 2: + copy_v3_fl(rgba, rf[0]); + rgba[3] = rf[1]; + break; + case 1: + copy_v3_fl(rgba, rf[0]); + rgba[3] = 1.0f; + break; + default: + BLI_assert(0); + } + } + else { + for (int c = 4; c--;) + rgba[c] = rc[c] * INV_255; + } + + /* we still need luma for histogram */ + luma = IMB_colormanagement_get_luminance(rgba); + + /* check for min max */ + if (ycc_mode == -1) { + minmax_v3v3_v3(min, max, rgba); + } + else { + rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); + mul_v3_fl(ycc, INV_255); + minmax_v3v3_v3(min, max, ycc); + } + /* increment count for histo*/ + bin_lum[get_bin_float(luma)]++; + bin_r[get_bin_float(rgba[0])]++; + bin_g[get_bin_float(rgba[1])]++; + bin_b[get_bin_float(rgba[2])]++; + bin_a[get_bin_float(rgba[3])]++; + + /* save sample if needed */ + if (do_sample_line) { + const float fx = (float)x / (float)ibuf->x; + const int idx = 2 * (ibuf->x * savedlines + x); + save_sample_line(scopes, idx, fx, rgba, ycc); + } + + rf += ibuf->channels; + rc += ibuf->channels; + } +} + +static void scopes_update_finalize(void *userdata, void *userdata_chunk) +{ + const ScopesUpdateData *data = userdata; + const ScopesUpdateDataChunk *data_chunk = userdata_chunk; + + unsigned int *bin_lum = data->bin_lum; + unsigned int *bin_r = data->bin_r; + unsigned int *bin_g = data->bin_g; + unsigned int *bin_b = data->bin_b; + unsigned int *bin_a = data->bin_a; + const unsigned int *bin_lum_c = data_chunk->bin_lum; + const unsigned int *bin_r_c = data_chunk->bin_r; + const unsigned int *bin_g_c = data_chunk->bin_g; + const unsigned int *bin_b_c = data_chunk->bin_b; + const unsigned int *bin_a_c = data_chunk->bin_a; + + float (*minmax)[2] = data->scopes->minmax; + const float *min = data_chunk->min; + const float *max = data_chunk->max; + + for (int b = 256; b--;) { + bin_lum[b] += bin_lum_c[b]; + bin_r[b] += bin_r_c[b]; + bin_g[b] += bin_g_c[b]; + bin_b[b] += bin_b_c[b]; + bin_a[b] += bin_a_c[b]; + } + + for (int c = 3; c--;) { + if (min[c] < minmax[c][0]) + minmax[c][0] = min[c]; + if (max[c] > minmax[c][1]) + minmax[c][1] = max[c]; + } +} + void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { -#ifdef _OPENMP - const int num_threads = BLI_system_thread_count(); -#endif - int a, y; + int a; unsigned int nl, na, nr, ng, nb; double divl, diva, divr, divg, divb; - unsigned char *display_buffer; + const unsigned char *display_buffer = NULL; unsigned int bin_lum[256] = {0}, bin_r[256] = {0}, bin_g[256] = {0}, bin_b[256] = {0}, bin_a[256] = {0}; - unsigned int bin_lum_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_r_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_g_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_b_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_a_t[BLENDER_MAX_THREADS][256] = {{0}}; int ycc_mode = -1; - const bool is_float = (ibuf->rect_float != NULL); void *cache_handle = NULL; struct ColormanageProcessor *cm_processor = NULL; - int rows_per_sample_line; if (ibuf->rect == NULL && ibuf->rect_float == NULL) return; @@ -1151,7 +1287,6 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings * scopes->sample_lines = ibuf->y; /* scan the image */ - rows_per_sample_line = ibuf->y / scopes->sample_lines; for (a = 0; a < 3; a++) { scopes->minmax[a][0] = 25500.0f; scopes->minmax[a][1] = -25500.0f; @@ -1177,129 +1312,21 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings * cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); } else { - display_buffer = (unsigned char *)IMB_display_buffer_acquire(ibuf, - view_settings, - display_settings, - &cache_handle); + display_buffer = (const unsigned char *)IMB_display_buffer_acquire( + ibuf, view_settings, display_settings, &cache_handle); } /* Keep number of threads in sync with the merge parts below. */ -#pragma omp parallel for private(y) schedule(static) num_threads(num_threads) if (ibuf->y > 256) - for (y = 0; y < ibuf->y; y++) { -#ifdef _OPENMP - const int thread_idx = omp_get_thread_num(); -#else - const int thread_idx = 0; -#endif - const float *rf = NULL; - const unsigned char *rc = NULL; - const int savedlines = y / rows_per_sample_line; - const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; - float min[3] = { FLT_MAX, FLT_MAX, FLT_MAX}, - max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX}; - int x, c; - if (is_float) - rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; - else { - rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; - } - for (x = 0; x < ibuf->x; x++) { - float rgba[4], ycc[3], luma; - if (is_float) { - - switch (ibuf->channels) { - case 4: - copy_v4_v4(rgba, rf); - IMB_colormanagement_processor_apply_v4(cm_processor, rgba); - break; - case 3: - copy_v3_v3(rgba, rf); - IMB_colormanagement_processor_apply_v3(cm_processor, rgba); - rgba[3] = 1.0f; - break; - case 2: - copy_v3_fl(rgba, rf[0]); - rgba[3] = rf[1]; - break; - case 1: - copy_v3_fl(rgba, rf[0]); - rgba[3] = 1.0f; - break; - default: - BLI_assert(0); - } - } - else { - for (c = 0; c < 4; c++) - rgba[c] = rc[c] * INV_255; - } - - /* we still need luma for histogram */ - luma = IMB_colormanagement_get_luminance(rgba); - - /* check for min max */ - if (ycc_mode == -1) { - for (c = 0; c < 3; c++) { - if (rgba[c] < min[c]) min[c] = rgba[c]; - if (rgba[c] > max[c]) max[c] = rgba[c]; - } - } - else { - rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); - for (c = 0; c < 3; c++) { - ycc[c] *= INV_255; - if (ycc[c] < min[c]) min[c] = ycc[c]; - if (ycc[c] > max[c]) max[c] = ycc[c]; - } - } - /* increment count for histo*/ - bin_lum_t[thread_idx][get_bin_float(luma)] += 1; - bin_r_t[thread_idx][get_bin_float(rgba[0])] += 1; - bin_g_t[thread_idx][get_bin_float(rgba[1])] += 1; - bin_b_t[thread_idx][get_bin_float(rgba[2])] += 1; - bin_a_t[thread_idx][get_bin_float(rgba[3])] += 1; - - /* save sample if needed */ - if (do_sample_line) { - const float fx = (float)x / (float)ibuf->x; - const int idx = 2 * (ibuf->x * savedlines + x); - save_sample_line(scopes, idx, fx, rgba, ycc); - } - - rf += ibuf->channels; - rc += ibuf->channels; - } -#pragma omp critical - { - for (c = 0; c < 3; c++) { - if (min[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = min[c]; - if (max[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = max[c]; - } - } - } - -#ifdef _OPENMP - if (ibuf->y > 256) { - for (a = 0; a < num_threads; a++) { - int b; - for (b = 0; b < 256; b++) { - bin_lum[b] += bin_lum_t[a][b]; - bin_r[b] += bin_r_t[a][b]; - bin_g[b] += bin_g_t[a][b]; - bin_b[b] += bin_b_t[a][b]; - bin_a[b] += bin_a_t[a][b]; - } - } - } - else -#endif - { - memcpy(bin_lum, bin_lum_t[0], sizeof(bin_lum)); - memcpy(bin_r, bin_r_t[0], sizeof(bin_r)); - memcpy(bin_g, bin_g_t[0], sizeof(bin_g)); - memcpy(bin_b, bin_b_t[0], sizeof(bin_b)); - memcpy(bin_a, bin_a_t[0], sizeof(bin_a)); - } + ScopesUpdateData data = { + .scopes = scopes, . ibuf = ibuf, + .cm_processor = cm_processor, .display_buffer = display_buffer, .ycc_mode = ycc_mode, + .bin_lum = bin_lum, .bin_r = bin_r, .bin_g = bin_g, .bin_b = bin_b, .bin_a = bin_a, + }; + ScopesUpdateDataChunk data_chunk = {0}; + INIT_MINMAX(data_chunk.min, data_chunk.max); + + BLI_task_parallel_range_finalize(0, ibuf->y, &data, &data_chunk, sizeof(data_chunk), + scopes_update_cb, scopes_update_finalize, ibuf->y > 256, false); /* test for nicer distribution even - non standard, leave it out for a while */ #if 0 diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index e855f6faa22..518d8d68919 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -44,6 +44,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_task.h" #include "BKE_shrinkwrap.h" #include "BKE_DerivedMesh.h" @@ -58,7 +59,7 @@ /* for timing... */ #if 0 -# include "PIL_time.h" +# include "PIL_time_utildefines.h" #else # define TIMEIT_BENCH(expr, id) (expr) #endif @@ -66,16 +67,88 @@ /* Util macros */ #define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n")) +typedef struct ShrinkwrapCalcCBData { + ShrinkwrapCalcData *calc; + + void *treeData; + void *auxData; + BVHTree *targ_tree; + BVHTree *aux_tree; + void *targ_callback; + void *aux_callback; + + float *proj_axis; + SpaceTransform *local2aux; +} ShrinkwrapCalcCBData; + /* * Shrinkwrap to the nearest vertex * * it builds a kdtree of vertexs we can attach to and then * for each vertex performs a nearest vertex search on the tree */ -static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) +static void shrinkwrap_calc_nearest_vertex_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) { - int i; + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + BVHTreeFromMesh *treeData = data->treeData; + BVHTreeNearest *nearest = userdata_chunk; + + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + /* Convert the vertex to tree coordinates */ + if (calc->vert) { + copy_v3_v3(tmp_co, calc->vert[i].co); + } + else { + copy_v3_v3(tmp_co, co); + } + BLI_space_transform_apply(&calc->local2target, tmp_co); + + /* Use local proximity heuristics (to reduce the nearest search) + * + * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + * so we can initiate the "nearest.dist" with the expected value to that last hit. + * This will lead in pruning of the search tree. */ + if (nearest->index != -1) + nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co); + else + nearest->dist_sq = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData); + + + /* Found the nearest vertex */ + if (nearest->index != -1) { + /* Adjusting the vertex weight, + * so that after interpolating it keeps a certain distance from the nearest position */ + if (nearest->dist_sq > FLT_EPSILON) { + const float dist = sqrtf(nearest->dist_sq); + weight *= (dist - calc->keepDist) / dist; + } + + /* Convert the coordinates back to mesh coordinates */ + copy_v3_v3(tmp_co, nearest->co); + BLI_space_transform_invert(&calc->local2target, tmp_co); + + interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ + } +} +static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) +{ BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; @@ -89,61 +162,11 @@ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) /* Setup nearest */ nearest.index = -1; nearest.dist_sq = FLT_MAX; -#ifndef __APPLE__ -#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(treeData, calc) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) { - continue; - } - - - /* Convert the vertex to tree coordinates */ - if (calc->vert) { - copy_v3_v3(tmp_co, calc->vert[i].co); - } - else { - copy_v3_v3(tmp_co, co); - } - BLI_space_transform_apply(&calc->local2target, tmp_co); - - /* Use local proximity heuristics (to reduce the nearest search) - * - * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex - * so we can initiate the "nearest.dist" with the expected value to that last hit. - * This will lead in pruning of the search tree. */ - if (nearest.index != -1) - nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); - else - nearest.dist_sq = FLT_MAX; - - BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); - - - /* Found the nearest vertex */ - if (nearest.index != -1) { - /* Adjusting the vertex weight, - * so that after interpolating it keeps a certain distance from the nearest position */ - if (nearest.dist_sq > FLT_EPSILON) { - const float dist = sqrtf(nearest.dist_sq); - weight *= (dist - calc->keepDist) / dist; - } - /* Convert the coordinates back to mesh coordinates */ - copy_v3_v3(tmp_co, nearest.co); - BLI_space_transform_invert(&calc->local2target, tmp_co); - - interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ - } - } + ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &nearest, sizeof(nearest), shrinkwrap_calc_nearest_vertex_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); free_bvhtree_from_mesh(&treeData); } @@ -230,13 +253,109 @@ bool BKE_shrinkwrap_project_normal( return false; } +static void shrinkwrap_calc_normal_projection_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) +{ + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + void *treeData = data->treeData; + void *auxData = data->auxData; + BVHTree *targ_tree = data->targ_tree; + BVHTree *aux_tree = data->aux_tree; + void *targ_callback = data->targ_callback; + void *aux_callback = data->aux_callback; + + float *proj_axis = data->proj_axis; + SpaceTransform *local2aux = data->local2aux; + + BVHTreeRayHit *hit = userdata_chunk; + + const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; + float *co = calc->vertexCos[i]; + float tmp_co[3], tmp_no[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + if (calc->vert) { + /* calc->vert contains verts from derivedMesh */ + /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ + /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ + if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { + copy_v3_v3(tmp_co, calc->vert[i].co); + normal_short_to_float_v3(tmp_no, calc->vert[i].no); + } + else { + copy_v3_v3(tmp_co, co); + copy_v3_v3(tmp_no, proj_axis); + } + } + else { + copy_v3_v3(tmp_co, co); + copy_v3_v3(tmp_no, proj_axis); + } + + + hit->index = -1; + hit->dist = BVH_RAYCAST_DIST_MAX; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ + + /* Project over positive direction of axis */ + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, tmp_no, + local2aux, aux_tree, hit, + aux_callback, auxData); + } + + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, tmp_no, + &calc->local2target, targ_tree, hit, + targ_callback, treeData); + } + + /* Project over negative direction of axis */ + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { + float inv_no[3]; + negate_v3_v3(inv_no, tmp_no); + + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, inv_no, + local2aux, aux_tree, hit, + aux_callback, auxData); + } + + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, inv_no, + &calc->local2target, targ_tree, hit, + targ_callback, treeData); + } + + /* don't set the initial dist (which is more efficient), + * because its calculated in the targets space, we want the dist in our own space */ + if (proj_limit_squared != 0.0f) { + if (len_squared_v3v3(hit->co, co) > proj_limit_squared) { + hit->index = -1; + } + } + + if (hit->index != -1) { + madd_v3_v3v3fl(hit->co, hit->co, tmp_no, calc->keepDist); + interp_v3_v3v3(co, co, hit->co, weight); + } +} static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for_render) { - int i; - /* Options about projection direction */ - const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; /* Raycast and tree stuff */ @@ -305,7 +424,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } if (targ_tree) { BVHTree *aux_tree = NULL; - void *aux_callback; + void *aux_callback = NULL; if (auxMesh != NULL) { /* use editmesh to avoid array allocation */ if (calc->smd->auxTarget && auxMesh->type == DM_TYPE_EDITBMESH) { @@ -316,99 +435,22 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } } else { - if ((aux_tree = bvhtree_from_mesh_looptri(&dmauxdata_stack, calc->target, 0.0, 4, 6)) != NULL) { + if ((aux_tree = bvhtree_from_mesh_looptri(&dmauxdata_stack, auxMesh, 0.0, 4, 6)) != NULL) { aux_callback = dmauxdata_stack.raycast_callback; auxData = &dmauxdata_stack; } } } /* After sucessufuly build the trees, start projection vertexs */ - -#ifndef __APPLE__ -#pragma omp parallel for private(i, hit) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3], tmp_no[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) { - continue; - } - - if (calc->vert) { - /* calc->vert contains verts from derivedMesh */ - /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ - /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ - if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { - copy_v3_v3(tmp_co, calc->vert[i].co); - normal_short_to_float_v3(tmp_no, calc->vert[i].no); - } - else { - copy_v3_v3(tmp_co, co); - copy_v3_v3(tmp_no, proj_axis); - } - } - else { - copy_v3_v3(tmp_co, co); - copy_v3_v3(tmp_no, proj_axis); - } - - - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ - - /* Project over positive direction of axis */ - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { - - if (aux_tree) { - BKE_shrinkwrap_project_normal( - 0, tmp_co, tmp_no, - &local2aux, aux_tree, &hit, - aux_callback, auxData); - } - - BKE_shrinkwrap_project_normal( - calc->smd->shrinkOpts, tmp_co, tmp_no, - &calc->local2target, targ_tree, &hit, - targ_callback, treeData); - } - - /* Project over negative direction of axis */ - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { - float inv_no[3]; - negate_v3_v3(inv_no, tmp_no); - - if (aux_tree) { - BKE_shrinkwrap_project_normal( - 0, tmp_co, inv_no, - &local2aux, aux_tree, &hit, - aux_callback, auxData); - } - - BKE_shrinkwrap_project_normal( - calc->smd->shrinkOpts, tmp_co, inv_no, - &calc->local2target, targ_tree, &hit, - targ_callback, treeData); - } - - /* don't set the initial dist (which is more efficient), - * because its calculated in the targets space, we want the dist in our own space */ - if (proj_limit_squared != 0.0f) { - if (len_squared_v3v3(hit.co, co) > proj_limit_squared) { - hit.index = -1; - } - } - - if (hit.index != -1) { - madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); - interp_v3_v3v3(co, co, hit.co, weight); - } - } + ShrinkwrapCalcCBData data = { + .calc = calc, + .treeData = treeData, .targ_tree = targ_tree, .targ_callback = targ_callback, + .auxData = auxData, .aux_tree = aux_tree, .aux_callback = aux_callback, + .proj_axis = proj_axis, .local2aux = &local2aux, + }; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &hit, sizeof(hit), shrinkwrap_calc_normal_projection_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); } /* free data structures */ @@ -428,10 +470,75 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for * it builds a BVHTree from the target mesh and then performs a * NN matches for each vertex */ -static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) +static void shrinkwrap_calc_nearest_surface_point_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) { - int i; + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + BVHTreeFromMesh *treeData = data->treeData; + BVHTreeNearest *nearest = userdata_chunk; + + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + /* Convert the vertex to tree coordinates */ + if (calc->vert) { + copy_v3_v3(tmp_co, calc->vert[i].co); + } + else { + copy_v3_v3(tmp_co, co); + } + BLI_space_transform_apply(&calc->local2target, tmp_co); + + /* Use local proximity heuristics (to reduce the nearest search) + * + * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + * so we can initiate the "nearest.dist" with the expected value to that last hit. + * This will lead in pruning of the search tree. */ + if (nearest->index != -1) + nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co); + else + nearest->dist_sq = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData); + + /* Found the nearest vertex */ + if (nearest->index != -1) { + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { + /* Make the vertex stay on the front side of the face */ + madd_v3_v3v3fl(tmp_co, nearest->co, nearest->no, calc->keepDist); + } + else { + /* Adjusting the vertex weight, + * so that after interpolating it keeps a certain distance from the nearest position */ + const float dist = sasqrt(nearest->dist_sq); + if (dist > FLT_EPSILON) { + /* linear interpolation */ + interp_v3_v3v3(tmp_co, tmp_co, nearest->co, (dist - calc->keepDist) / dist); + } + else { + copy_v3_v3(tmp_co, nearest->co); + } + } + /* Convert the coordinates back to mesh coordinates */ + BLI_space_transform_invert(&calc->local2target, tmp_co); + interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ + } +} + +static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) +{ BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; @@ -446,67 +553,11 @@ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) nearest.index = -1; nearest.dist_sq = FLT_MAX; - /* Find the nearest vertex */ -#ifndef __APPLE__ -#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc, treeData) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) continue; - - /* Convert the vertex to tree coordinates */ - if (calc->vert) { - copy_v3_v3(tmp_co, calc->vert[i].co); - } - else { - copy_v3_v3(tmp_co, co); - } - BLI_space_transform_apply(&calc->local2target, tmp_co); - - /* Use local proximity heuristics (to reduce the nearest search) - * - * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex - * so we can initiate the "nearest.dist" with the expected value to that last hit. - * This will lead in pruning of the search tree. */ - if (nearest.index != -1) - nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); - else - nearest.dist_sq = FLT_MAX; - - BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); - - /* Found the nearest vertex */ - if (nearest.index != -1) { - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { - /* Make the vertex stay on the front side of the face */ - madd_v3_v3v3fl(tmp_co, nearest.co, nearest.no, calc->keepDist); - } - else { - /* Adjusting the vertex weight, - * so that after interpolating it keeps a certain distance from the nearest position */ - const float dist = sasqrt(nearest.dist_sq); - if (dist > FLT_EPSILON) { - /* linear interpolation */ - interp_v3_v3v3(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist) / dist); - } - else { - copy_v3_v3(tmp_co, nearest.co); - } - } - - /* Convert the coordinates back to mesh coordinates */ - BLI_space_transform_invert(&calc->local2target, tmp_co); - interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ - } - } + ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &nearest, sizeof(nearest), shrinkwrap_calc_nearest_surface_point_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); free_bvhtree_from_mesh(&treeData); } diff --git a/source/blender/blenlib/BLI_array_store.h b/source/blender/blenlib/BLI_array_store.h new file mode 100644 index 00000000000..f4cbc07bf26 --- /dev/null +++ b/source/blender/blenlib/BLI_array_store.h @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_ARRAY_STORE_H__ +#define __BLI_ARRAY_STORE_H__ + +/** \file BLI_array_store.h + * \ingroup bli + * \brief Efficient in-memory storage of multiple similar arrays. + */ + +typedef struct BArrayStore BArrayStore; +typedef struct BArrayState BArrayState; + +BArrayStore *BLI_array_store_create( + unsigned int stride, unsigned int chunk_count); +void BLI_array_store_destroy( + BArrayStore *bs); +void BLI_array_store_clear( + BArrayStore *bs); + +/* find the memory used by all states (expanded & real) */ +size_t BLI_array_store_calc_size_expanded_get( + const BArrayStore *bs); +size_t BLI_array_store_calc_size_compacted_get( + const BArrayStore *bs); + +BArrayState *BLI_array_store_state_add( + BArrayStore *bs, + const void *data, const size_t data_len, + const BArrayState *state_reference); +void BLI_array_store_state_remove( + BArrayStore *bs, + BArrayState *state); + +size_t BLI_array_store_state_size_get( + BArrayState *state); +void BLI_array_store_state_data_get( + BArrayState *state, + void *data); +void *BLI_array_store_state_data_get_alloc( + BArrayState *state, + size_t *r_data_len); + +/* only for tests */ +bool BLI_array_store_is_valid( + BArrayStore *bs); + +#endif /* __BLI_ARRAY_STORE_H__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 944ba60eb58..42d958774d8 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC intern/BLI_memarena.c intern/BLI_mempool.c intern/DLRB_tree.c + intern/array_store.c intern/array_utils.c intern/astar.c intern/boxpack2d.c @@ -120,6 +121,7 @@ set(SRC BLI_alloca.h BLI_args.h BLI_array.h + BLI_array_store.h BLI_array_utils.h BLI_astar.h BLI_bitmap.h diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 06946e520a8..05f2d9221ef 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -1287,7 +1287,7 @@ bool BLI_ghashutil_paircmp(const void *a, const void *b) const GHashPair *A = a; const GHashPair *B = b; - return (BLI_ghashutil_ptrcmp(A->first, B->first) || + return (BLI_ghashutil_ptrcmp(A->first, B->first) && BLI_ghashutil_ptrcmp(A->second, B->second)); } diff --git a/source/blender/blenlib/intern/array_store.c b/source/blender/blenlib/intern/array_store.c new file mode 100644 index 00000000000..7f657f4a048 --- /dev/null +++ b/source/blender/blenlib/intern/array_store.c @@ -0,0 +1,1731 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenlib/intern/array_store.c + * \ingroup bli + * \brief Array storage to minimize duplication. + * + * This is done by splitting arrays into chunks and using copy-on-write (COW), + * to de-duplicate chunks, + * from the users perspective this is an implementation detail. + * + * + * Overview + * ======== + * + * + * Data Structure + * -------------- + * + * This diagram is an overview of the structure of a single array-store. + * + * \note The only 2 structues here which are referenced externally are the. + * + * - BArrayStore: The whole array store. + * - BArrayState: Represents a single state (array) of data. + * These can be add using a reference state, while this could be considered the previous or parent state. + * no relationship is kept, so the caller is free to add any state from the same BArrayStore as a reference. + * + * <pre> + * <+> BArrayStore: root data-structure, + * | can store many 'states', which share memory. + * | + * | This can store many arrays, however they must share the same 'stride'. + * | Arrays of different types will need to use a new BArrayStore. + * | + * +- <+> states (Collection of BArrayState's): + * | | Each represents an array added by the user of this API. + * | | and references a chunk_list (each state is a chunk_list user). + * | | Note that the list order has no significance. + * | | + * | +- <+> chunk_list (BChunkList): + * | | The chunks that make up this state. + * | | Each state is a chunk_list user, + * | | avoids duplicating lists when there is no change between states. + * | | + * | +- chunk_refs (List of BChunkRef): Each chunk_ref links to a a BChunk. + * | Each reference is a chunk user, + * | avoids duplicating smaller chunks of memory found in multiple states. + * | + * +- info (BArrayInfo): + * | Sizes and offsets for this array-store. + * | Also caches some variables for reuse. + * | + * +- <+> memory (BArrayMemory): + * | Memory pools for storing BArrayStore data. + * | + * +- chunk_list (Pool of BChunkList): + * | All chunk_lists, (reference counted, used by BArrayState). + * | + * +- chunk_ref (Pool of BChunkRef): + * | All chunk_refs (link between BChunkList & BChunk). + * | + * +- chunks (Pool of BChunk): + * All chunks, (reference counted, used by BChunkList). + * These have their headers hashed for reuse so we can quickly check for duplicates. + * </pre> + * + * + * De-Duplication + * -------------- + * + * When creating a new state, a previous state can be given as a reference, + * matching chunks from this state are re-used in the new state. + * + * First matches at either end of the array are detected. + * For identical arrays this is all thats needed. + * + * De-duplication is performed on any remaining chunks, by hasing the first few bytes of the chunk + * (see: BCHUNK_HASH_TABLE_ACCUMULATE_STEPS). + * + * \note This is cached for reuse since the referenced data never changes. + * + * An array is created to store hash values at every 'stride', + * then stepped over to search for matching chunks. + * + * Once a match is found, there is a high chance next chunks match too, + * so this is checked to avoid performing so many hash-lookups. + * Otherwise new chunks are created. + */ + +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_mempool.h" + +#include "BLI_strict_flags.h" + +#include "BLI_array_store.h" /* own include */ + +/* only for BLI_array_store_is_valid */ +#include "BLI_ghash.h" + +/** \name Defines + * + * Some of the logic for merging is quite involved, + * support disabling some parts of this. + * \{ */ + +/* Scan first chunks (happy path when beginning of the array matches). + * When the array is a perfect match, we can re-use the entire list. + * + * Note that disabling makes some tests fail that check for output-size. + */ +#define USE_FASTPATH_CHUNKS_FIRST + +/* Scan last chunks (happy path when end of the array matches). + * When the end of the array matches, we can quickly add these chunks. + * note that we will add contiguous matching chunks + * so this isn't as useful as USE_FASTPATH_CHUNKS_FIRST, + * however it avoids adding matching chunks into the lookup table, + * so creating the lookup table won't be as expensive. + */ +#ifdef USE_FASTPATH_CHUNKS_FIRST +# define USE_FASTPATH_CHUNKS_LAST +#endif + +/* For arrays of matching length, test that *enough* of the chunks are aligned, + * and simply step over both arrays, using matching chunks. + * This avoids overhead of using a lookup table for cases when we can assume they're mostly aligned. + */ +#define USE_ALIGN_CHUNKS_TEST + +/* Accumulate hashes from right to left so we can create a hash for the chunk-start. + * This serves to increase uniqueness and will help when there is many values which are the same. + */ +#define USE_HASH_TABLE_ACCUMULATE + +#ifdef USE_HASH_TABLE_ACCUMULATE +/* Number of times to propagate hashes back. + * Effectively a 'triangle-number'. + * so 4 -> 7, 5 -> 10, 6 -> 15... etc. + */ +# define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS 4 +#else +/* How many items to hash (multiplied by stride) + */ +# define BCHUNK_HASH_LEN 4 +#endif + +/* Calculate the key once and reuse it + */ +#define USE_HASH_TABLE_KEY_CACHE +#ifdef USE_HASH_TABLE_KEY_CACHE +# define HASH_TABLE_KEY_UNSET ((uint64_t)-1) +# define HASH_TABLE_KEY_FALLBACK ((uint64_t)-2) +#endif + +/* How much larger the table is then the total number of chunks. + */ +#define BCHUNK_HASH_TABLE_MUL 3 + +/* Merge too small/large chunks: + * + * Using this means chunks below a threshold will be merged together. + * Even though short term this uses more memory, + * long term the overhead of maintaining many small chunks is reduced. + * This is defined by setting the minimum chunk size (as a fraction of the regular chunk size). + * + * Chunks may also become too large (when incrementally growing an array), + * this also enables chunk splitting. + */ +#define USE_MERGE_CHUNKS + +#ifdef USE_MERGE_CHUNKS +/* Merge chunks smaller then: (chunk_size / BCHUNK_MIN_SIZE_DIV) + */ +# define BCHUNK_SIZE_MIN_DIV 8 + +/* Disallow chunks bigger then the regular chunk size scaled by this value + * note: must be at least 2! + * however, this code runs wont run in tests unless its ~1.1 ugh. + * so lower only to check splitting works. + */ +# define BCHUNK_SIZE_MAX_MUL 2 +#endif /* USE_MERGE_CHUNKS */ + +/* slow (keep disabled), but handy for debugging */ +// #define USE_VALIDATE_LIST_SIZE + +// #define USE_VALIDATE_LIST_DATA_PARTIAL + +// #define USE_PARANOID_CHECKS + +/** \} */ + + +/** \name Internal Structs + * \{ */ + +typedef unsigned int uint; +typedef unsigned char ubyte; + +typedef uint64_t hash_key; + + +typedef struct BArrayInfo { + size_t chunk_stride; + uint chunk_count; + + /* pre-calculated */ + size_t chunk_byte_size; + size_t chunk_byte_size_min; + + size_t accum_read_ahead_bytes; +#ifdef USE_HASH_TABLE_ACCUMULATE + size_t accum_steps; + size_t accum_read_ahead_len; +#endif +} BArrayInfo; + +typedef struct BArrayMemory { + BLI_mempool *chunk_list; /* BChunkList */ + BLI_mempool *chunk_ref; /* BChunkRef */ + BLI_mempool *chunk; /* BChunk */ +} BArrayMemory; + +/** + * Main storage for all states + */ +typedef struct BArrayStore { + /* static */ + BArrayInfo info; + + /* memory storage */ + BArrayMemory memory; + + /** + * #BArrayState may be in any order (logic should never depend on state order). + */ + ListBase states; +} BArrayStore; + +/** + * A single instance of an array. + * + * This is how external API's hold a reference to an in-memory state, + * although the struct is private. + * + * \note Currently each 'state' is allocated separately. + * While this could be moved to a memory pool, + * it makes it easier to trace invalid usage, so leave as-is for now. + */ +typedef struct BArrayState { + /** linked list in #BArrayStore.states */ + struct BArrayState *next, *prev; + + struct BChunkList *chunk_list; /* BChunkList's */ + +} BArrayState; + +typedef struct BChunkList { + ListBase chunk_refs; /* BChunkRef's */ + uint chunk_refs_len; /* BLI_listbase_count(chunks), store for reuse. */ + size_t total_size; /* size of all chunks */ + + /** number of #BArrayState using this. */ + int users; +} BChunkList; + +/* a chunk of an array */ +typedef struct BChunk { + const ubyte *data; + size_t data_len; + /** number of #BChunkList using this. */ + int users; + +#ifdef USE_HASH_TABLE_KEY_CACHE + hash_key key; +#endif +} BChunk; + +/** + * Links to store #BChunk data in #BChunkList.chunks. + */ +typedef struct BChunkRef { + struct BChunkRef *next, *prev; + BChunk *link; +} BChunkRef; + +/** + * Single linked list used when putting chunks into a temporary table, + * used for lookups. + * + * Point to the #BChunkRef, not the #BChunk, + * to allow talking down the chunks in-order until a mis-match is found, + * this avoids having to do so many table lookups. + */ +typedef struct BTableRef { + struct BTableRef *next; + const BChunkRef *cref; +} BTableRef; + +/** \} */ + + +static size_t bchunk_list_size(const BChunkList *chunk_list); + + +/** \name Internal BChunk API + * \{ */ + +static BChunk *bchunk_new( + BArrayMemory *bs_mem, const ubyte *data, const size_t data_len) +{ + BChunk *chunk = BLI_mempool_alloc(bs_mem->chunk); + chunk->data = data; + chunk->data_len = data_len; + chunk->users = 0; +#ifdef USE_HASH_TABLE_KEY_CACHE + chunk->key = HASH_TABLE_KEY_UNSET; +#endif + return chunk; +} + +static BChunk *bchunk_new_copydata( + BArrayMemory *bs_mem, const ubyte *data, const size_t data_len) +{ + ubyte *data_copy = MEM_mallocN(data_len, __func__); + memcpy(data_copy, data, data_len); + return bchunk_new(bs_mem, data_copy, data_len); +} + +static void bchunk_decref( + BArrayMemory *bs_mem, BChunk *chunk) +{ + BLI_assert(chunk->users > 0); + if (chunk->users == 1) { + MEM_freeN((void *)chunk->data); + BLI_mempool_free(bs_mem->chunk, chunk); + } + else { + chunk->users -= 1; + } +} + +static bool bchunk_data_compare( + const BChunk *chunk, + const ubyte *data_base, const size_t data_base_len, + const size_t offset) +{ + if (offset + (size_t)chunk->data_len <= data_base_len) { + return (memcmp(&data_base[offset], chunk->data, chunk->data_len) == 0); + } + else { + return false; + } +} + +/** \} */ + + +/** \name Internal BChunkList API + * \{ */ + +static BChunkList *bchunk_list_new( + BArrayMemory *bs_mem, size_t total_size) +{ + BChunkList *chunk_list = BLI_mempool_alloc(bs_mem->chunk_list); + + BLI_listbase_clear(&chunk_list->chunk_refs); + chunk_list->chunk_refs_len = 0; + chunk_list->total_size = total_size; + chunk_list->users = 0; + return chunk_list; +} + +static void bchunk_list_decref( + BArrayMemory *bs_mem, BChunkList *chunk_list) +{ + BLI_assert(chunk_list->users > 0); + if (chunk_list->users == 1) { + for (BChunkRef *cref = chunk_list->chunk_refs.first, *cref_next; cref; cref = cref_next) { + cref_next = cref->next; + bchunk_decref(bs_mem, cref->link); + BLI_mempool_free(bs_mem->chunk_ref, cref); + } + + BLI_mempool_free(bs_mem->chunk_list, chunk_list); + } + else { + chunk_list->users -= 1; + } +} + +#ifdef USE_VALIDATE_LIST_SIZE +# ifndef NDEBUG +# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) BLI_assert(bchunk_list_size(chunk_list) == n) +# endif +#endif +#ifndef ASSERT_CHUNKLIST_SIZE +# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) (EXPR_NOP(chunk_list), EXPR_NOP(n)) +#endif + + +#ifdef USE_VALIDATE_LIST_DATA_PARTIAL +static size_t bchunk_list_data_check( + const BChunkList *chunk_list, const ubyte *data) +{ + size_t total_size = 0; + for (BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + if (memcmp(&data[total_size], cref->link->data, cref->link->data_len) != 0) { + return false; + } + total_size += cref->link->data_len; + } + return true; +} +# define ASSERT_CHUNKLIST_DATA(chunk_list, data) BLI_assert(bchunk_list_data_check(chunk_list, data)) +#else +# define ASSERT_CHUNKLIST_DATA(chunk_list, data) (EXPR_NOP(chunk_list), EXPR_NOP(data)) +#endif + + +#ifdef USE_MERGE_CHUNKS +static void bchunk_list_ensure_min_size_last( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list) +{ + BChunkRef *cref = chunk_list->chunk_refs.last; + if (cref && cref->prev) { + /* both are decref'd after use (end of this block) */ + BChunk *chunk_curr = cref->link; + BChunk *chunk_prev = cref->prev->link; + + if (MIN2(chunk_prev->data_len, chunk_curr->data_len) < info->chunk_byte_size_min) { + const size_t data_merge_len = chunk_prev->data_len + chunk_curr->data_len; + /* we could pass, but no need */ + if (data_merge_len <= (info->chunk_byte_size * BCHUNK_SIZE_MAX_MUL)) { + /* we have enough space to merge */ + + /* remove last from linklist */ + BLI_assert(chunk_list->chunk_refs.last != chunk_list->chunk_refs.first); + cref->prev->next = NULL; + chunk_list->chunk_refs.last = cref->prev; + chunk_list->chunk_refs_len -= 1; + + ubyte *data_merge = MEM_mallocN(data_merge_len, __func__); + memcpy(data_merge, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_merge[chunk_prev->data_len], chunk_curr->data, chunk_curr->data_len); + + cref->prev->link = bchunk_new(bs_mem, data_merge, data_merge_len); + cref->prev->link->users += 1; + + BLI_mempool_free(bs_mem->chunk_ref, cref); + } + else { + /* If we always merge small slices, we should _almost_ never end up having very large chunks. + * Gradual expanding on contracting will cause this. + * + * if we do, the code below works (test by setting 'BCHUNK_SIZE_MAX_MUL = 1.2') */ + + /* keep chunk on the left hand side a regular size */ + const size_t split = info->chunk_byte_size; + + /* merge and split */ + const size_t data_prev_len = split; + const size_t data_curr_len = data_merge_len - split; + ubyte *data_prev = MEM_mallocN(data_prev_len, __func__); + ubyte *data_curr = MEM_mallocN(data_curr_len, __func__); + + if (data_prev_len <= chunk_prev->data_len) { + const size_t data_curr_shrink_len = chunk_prev->data_len - data_prev_len; + + /* setup 'data_prev' */ + memcpy(data_prev, chunk_prev->data, data_prev_len); + + /* setup 'data_curr' */ + memcpy(data_curr, &chunk_prev->data[data_prev_len], data_curr_shrink_len); + memcpy(&data_curr[data_curr_shrink_len], chunk_curr->data, chunk_curr->data_len); + } + else { + BLI_assert(data_curr_len <= chunk_curr->data_len); + BLI_assert(data_prev_len >= chunk_prev->data_len); + + const size_t data_prev_grow_len = data_prev_len - chunk_prev->data_len; + + /* setup 'data_prev' */ + memcpy(data_prev, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_prev[chunk_prev->data_len], chunk_curr->data, data_prev_grow_len); + + /* setup 'data_curr' */ + memcpy(data_curr, &chunk_curr->data[data_prev_grow_len], data_curr_len); + } + + cref->prev->link = bchunk_new(bs_mem, data_prev, data_prev_len); + cref->prev->link->users += 1; + + cref->link = bchunk_new(bs_mem, data_curr, data_curr_len); + cref->link->users += 1; + } + + /* free zero users */ + bchunk_decref(bs_mem, chunk_curr); + bchunk_decref(bs_mem, chunk_prev); + } + } +} +#endif /* USE_MERGE_CHUNKS */ + +/** + * Append and don't manage merging small chunks. + */ +static bool bchunk_list_append_only( + BArrayMemory *bs_mem, + BChunkList *chunk_list, BChunk *chunk) +{ + BChunkRef *cref = BLI_mempool_alloc(bs_mem->chunk_ref); + BLI_addtail(&chunk_list->chunk_refs, cref); + cref->link = chunk; + chunk_list->chunk_refs_len += 1; + chunk->users += 1; + return chunk; +} + +static void bchunk_list_append_data( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + const ubyte *data, const size_t data_len) +{ + BLI_assert(data_len != 0); + // printf("data_len: %d\n", data_len); +#ifdef USE_MERGE_CHUNKS + if (!BLI_listbase_is_empty(&chunk_list->chunk_refs)) { + BChunkRef *cref = chunk_list->chunk_refs.last; + BChunk *chunk_prev = cref->link; + + if (MIN2(chunk_prev->data_len, data_len) < info->chunk_byte_size_min) { + const size_t data_merge_len = chunk_prev->data_len + data_len; + /* realloc for single user */ + if (cref->link->users == 1) { + ubyte *data_merge = MEM_reallocN((void *)cref->link->data, data_merge_len); + memcpy(&data_merge[chunk_prev->data_len], data, data_len); + cref->link->data = data_merge; + cref->link->data_len = data_merge_len; + } + else { + ubyte *data_merge = MEM_mallocN(data_merge_len, __func__); + memcpy(data_merge, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_merge[chunk_prev->data_len], data, data_len); + cref->link = bchunk_new(bs_mem, data_merge, data_merge_len); + cref->link->users += 1; + bchunk_decref(bs_mem, chunk_prev); + } + return; + } + } +#else + UNUSED_VARS(info); +#endif /* USE_MERGE_CHUNKS */ + + BChunk *chunk = bchunk_new_copydata(bs_mem, data, data_len); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + + /* don't run this, instead preemptively avoid creating a chunk only to merge it (above). */ +#if 0 +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list, chunk_size_min); +#endif +#endif +} + +static void bchunk_list_append( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + BChunk *chunk) +{ + bchunk_list_append_only(bs_mem, chunk_list, chunk); + +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list); +#else + UNUSED_VARS(info); +#endif +} + +static void bchunk_list_fill_from_array( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + const ubyte *data, + const size_t data_len) +{ + BLI_assert(BLI_listbase_is_empty(&chunk_list->chunk_refs)); + + size_t data_last_chunk_len = 0; + size_t data_trim_len = data_len; + +#ifdef USE_MERGE_CHUNKS + /* avoid creating too-small chunks + * more efficient then merging after */ + if (data_len > info->chunk_byte_size) { + data_last_chunk_len = (data_trim_len % info->chunk_byte_size); + data_trim_len = data_trim_len - data_last_chunk_len; + if (data_last_chunk_len) { + if (data_last_chunk_len < info->chunk_byte_size_min) { + /* may be zero and thats OK */ + data_trim_len -= info->chunk_byte_size; + data_last_chunk_len += info->chunk_byte_size; + } + } + } + else { + data_trim_len = 0; + data_last_chunk_len = data_len; + } +#else + data_last_chunk_len = (data_trim_len % info->chunk_byte_size); + data_trim_len = data_trim_len - data_last_chunk_len; +#endif + + + BLI_assert(data_trim_len + data_last_chunk_len == data_len); + + size_t i_prev = 0; + while (i_prev < data_trim_len) { + const size_t i = i_prev + info->chunk_byte_size; + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], i - i_prev); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + i_prev = i; + } + + if (data_last_chunk_len) { + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], data_last_chunk_len); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + // i_prev = data_len; + } + +#ifdef USE_MERGE_CHUNKS + if (data_len > info->chunk_byte_size) { + BLI_assert(((BChunkRef *)chunk_list->chunk_refs.last)->link->data_len >= info->chunk_byte_size_min); + } +#endif + + /* works but better avoid redundant re-alloc */ +#if 0 +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list); +#endif +#endif + + ASSERT_CHUNKLIST_SIZE(chunk_list, data_len); + ASSERT_CHUNKLIST_DATA(chunk_list, data); +} + + +/* --------------------------------------------------------------------------- + * Internal Table Lookup Functions + */ + +/** \name Internal Hashing/De-Duplication API + * + * Only used by #bchunk_list_from_data_merge + * \{ */ + +#define HASH_INIT (5381) + +BLI_INLINE uint hash_data_single(const ubyte p) +{ + return (HASH_INIT << 5) + HASH_INIT + (unsigned int)p; +} + +/* hash bytes, from BLI_ghashutil_strhash_n */ +static uint hash_data(const ubyte *key, size_t n) +{ + const signed char *p; + unsigned int h = HASH_INIT; + + for (p = (const signed char *)key; n--; p++) { + h = (h << 5) + h + (unsigned int)*p; + } + + return h; +} + +#undef HASH_INIT + + +#ifdef USE_HASH_TABLE_ACCUMULATE +static void hash_array_from_data( + const BArrayInfo *info, const ubyte *data_slice, const size_t data_slice_len, + hash_key *hash_array) +{ + if (info->chunk_stride != 1) { + for (size_t i = 0, i_step = 0; i_step < data_slice_len; i++, i_step += info->chunk_stride) { + hash_array[i] = hash_data(&data_slice[i_step], info->chunk_stride); + } + } + else { + /* fast-path for bytes */ + for (size_t i = 0; i < data_slice_len; i++) { + hash_array[i] = hash_data_single(data_slice[i]); + } + } +} + +/* + * Similar to hash_array_from_data, + * but able to step into the next chunk if we run-out of data. + */ +static void hash_array_from_cref( + const BArrayInfo *info, const BChunkRef *cref, const size_t data_len, + hash_key *hash_array) +{ + const size_t hash_array_len = data_len / info->chunk_stride; + size_t i = 0; + do { + size_t i_next = hash_array_len - i; + size_t data_trim_len = i_next * info->chunk_stride; + if (data_trim_len > cref->link->data_len) { + data_trim_len = cref->link->data_len; + i_next = data_trim_len / info->chunk_stride; + } + BLI_assert(data_trim_len <= cref->link->data_len); + hash_array_from_data(info, cref->link->data, data_trim_len, &hash_array[i]); + i += i_next; + cref = cref->next; + } while ((i < hash_array_len) && (cref != NULL)); + + /* If this isn't equal, the caller didn't properly check + * that there was enough data left in all chunks */ + BLI_assert(i == hash_array_len); +} + +static void hash_accum(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps) +{ + /* _very_ unlikely, can happen if you select a chunk-size of 1 for example. */ + if (UNLIKELY((iter_steps > hash_array_len))) { + iter_steps = hash_array_len; + } + + const size_t hash_array_search_len = hash_array_len - iter_steps; + while (iter_steps != 0) { + const size_t hash_offset = iter_steps; + for (uint i = 0; i < hash_array_search_len; i++) { + hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1); + } + iter_steps -= 1; + } +} + +/** + * When we only need a single value, can use a small optimization. + * we can avoid accumulating the tail of the array a little, each iteration. + */ +static void hash_accum_single(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps) +{ + BLI_assert(iter_steps <= hash_array_len); + if (UNLIKELY(!(iter_steps <= hash_array_len))) { + /* while this shouldn't happen, avoid crashing */ + iter_steps = hash_array_len; + } + /* We can increase this value each step to avoid accumulating quite as much + * while getting the same results as hash_accum */ + size_t iter_steps_sub = iter_steps; + + while (iter_steps != 0) { + const size_t hash_array_search_len = hash_array_len - iter_steps_sub; + const size_t hash_offset = iter_steps; + for (uint i = 0; i < hash_array_search_len; i++) { + hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1); + } + iter_steps -= 1; + iter_steps_sub += iter_steps; + } +} + +static hash_key key_from_chunk_ref( + const BArrayInfo *info, const BChunkRef *cref, + /* avoid reallicating each time */ + hash_key *hash_store, const size_t hash_store_len) +{ + /* in C, will fill in a reusable array */ + BChunk *chunk = cref->link; + BLI_assert(info->accum_read_ahead_bytes * info->chunk_stride); + + if (info->accum_read_ahead_bytes <= chunk->data_len) { + hash_key key; + +#ifdef USE_HASH_TABLE_KEY_CACHE + key = chunk->key; + if (key != HASH_TABLE_KEY_UNSET) { + /* Using key cache! + * avoids calculating every time */ + } + else { + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + key = hash_store[0]; + + /* cache the key */ + if (key == HASH_TABLE_KEY_UNSET) { + key = HASH_TABLE_KEY_FALLBACK; + } + chunk->key = key; + } +#else + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + key = hash_store[0]; +#endif + return key; + } + else { + /* corner case - we're too small, calculate the key each time. */ + + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + hash_key key = hash_store[0]; + +#ifdef USE_HASH_TABLE_KEY_CACHE + if (UNLIKELY(key == HASH_TABLE_KEY_UNSET)) { + key = HASH_TABLE_KEY_FALLBACK; + } +#endif + return key; + } +} + +static const BChunkRef *table_lookup( + const BArrayInfo *info, BTableRef **table, const size_t table_len, const size_t i_table_start, + const ubyte *data, const size_t data_len, const size_t offset, const hash_key *table_hash_array) +{ + size_t size_left = data_len - offset; + hash_key key = table_hash_array[((offset - i_table_start) / info->chunk_stride)]; + size_t key_index = (size_t)(key % (hash_key)table_len); + for (BTableRef *tref = table[key_index]; tref; tref = tref->next) { + const BChunkRef *cref = tref->cref; +#ifdef USE_HASH_TABLE_KEY_CACHE + if (cref->link->key == key) +#endif + { + BChunk *chunk_test = cref->link; + if (chunk_test->data_len <= size_left) { + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + /* we could remove the chunk from the table, to avoid multiple hits */ + return cref; + } + } + } + } + return NULL; +} + +#else /* USE_HASH_TABLE_ACCUMULATE */ + +/* NON USE_HASH_TABLE_ACCUMULATE code (simply hash each chunk) */ + +static hash_key key_from_chunk_ref(const BArrayInfo *info, const BChunkRef *cref) +{ + const size_t data_hash_len = BCHUNK_HASH_LEN * info->chunk_stride; + hash_key key; + BChunk *chunk = cref->link; + +#ifdef USE_HASH_TABLE_KEY_CACHE + key = chunk->key; + if (key != HASH_TABLE_KEY_UNSET) { + /* Using key cache! + * avoids calculating every time */ + } + else { + /* cache the key */ + key = hash_data(chunk->data, data_hash_len); + if (key == HASH_TABLE_KEY_UNSET) { + key = HASH_TABLE_KEY_FALLBACK; + } + chunk->key = key; + } +#else + key = hash_data(chunk->data, data_hash_len); +#endif + + return key; +} + +static const BChunkRef *table_lookup( + const BArrayInfo *info, BTableRef **table, const size_t table_len, const uint UNUSED(i_table_start), + const ubyte *data, const size_t data_len, const size_t offset, const hash_key *UNUSED(table_hash_array)) +{ + const size_t data_hash_len = BCHUNK_HASH_LEN * info->chunk_stride; /* TODO, cache */ + + size_t size_left = data_len - offset; + hash_key key = hash_data(&data[offset], MIN2(data_hash_len, size_left)); + size_t key_index = (size_t)(key % (hash_key)table_len); + for (BTableRef *tref = table[key_index]; tref; tref = tref->next) { + const BChunkRef *cref = tref->cref; +#ifdef USE_HASH_TABLE_KEY_CACHE + if (cref->link->key == key) +#endif + { + BChunk *chunk_test = cref->link; + if (chunk_test->data_len <= size_left) { + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + /* we could remove the chunk from the table, to avoid multiple hits */ + return cref; + } + } + } + } + return NULL; +} + +#endif /* USE_HASH_TABLE_ACCUMULATE */ + +/* End Table Lookup + * ---------------- */ + +/** \} */ + +/** + * \param data: Data to store in the returned value. + * \param data_len_original: Length of data in bytes. + * \param chunk_list_reference: Reuse this list or chunks within it, don't modify its content. + * \note Caller is responsible for adding the user. + */ +static BChunkList *bchunk_list_from_data_merge( + const BArrayInfo *info, BArrayMemory *bs_mem, + const ubyte *data, const size_t data_len_original, + const BChunkList *chunk_list_reference) +{ + ASSERT_CHUNKLIST_SIZE(chunk_list_reference, chunk_list_reference->total_size); + + /* ----------------------------------------------------------------------- + * Fast-Path for exact match + * Check for exact match, if so, return the current list. + */ + + const BChunkRef *cref_match_first = NULL; + + uint chunk_list_reference_skip_len = 0; + size_t chunk_list_reference_skip_bytes = 0; + size_t i_prev = 0; + +#ifdef USE_FASTPATH_CHUNKS_FIRST + bool full_match = false; + + { + full_match = true; + + const BChunkRef *cref = chunk_list_reference->chunk_refs.first; + while (i_prev < data_len_original) { + if (cref != NULL && bchunk_data_compare(cref->link, data, data_len_original, i_prev)) { + cref_match_first = cref; + chunk_list_reference_skip_len += 1; + chunk_list_reference_skip_bytes += cref->link->data_len; + i_prev += cref->link->data_len; + cref = cref->next; + } + else { + full_match = false; + break; + } + } + + if (full_match) { + if (chunk_list_reference->total_size == data_len_original) { + return (BChunkList *)chunk_list_reference; + } + } + } + + /* End Fast-Path (first) + * --------------------- */ + +#endif /* USE_FASTPATH_CHUNKS_FIRST */ + + /* Copy until we have a mismatch */ + BChunkList *chunk_list = bchunk_list_new(bs_mem, data_len_original); + if (cref_match_first != NULL) { + size_t chunk_size_step = 0; + const BChunkRef *cref = chunk_list_reference->chunk_refs.first; + while (true) { + BChunk *chunk = cref->link; + chunk_size_step += chunk->data_len; + bchunk_list_append_only(bs_mem, chunk_list, chunk); + ASSERT_CHUNKLIST_SIZE(chunk_list, chunk_size_step); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + if (cref == cref_match_first) { + break; + } + else { + cref = cref->next; + } + } + /* happens when bytes are removed from the end of the array */ + if (chunk_size_step == data_len_original) { + return chunk_list; + } + + i_prev = chunk_size_step; + } + else { + i_prev = 0; + } + + /* ------------------------------------------------------------------------ + * Fast-Path for end chunks + * + * Check for trailing chunks + */ + + /* In this case use 'chunk_list_reference_last' to define the last index + * index_match_last = -1 */ + + /* warning, from now on don't use len(data) + * since we want to ignore chunks already matched */ + size_t data_len = data_len_original; +#define data_len_original invalid_usage +#ifdef data_len_original /* quiet warning */ +#endif + + const BChunkRef *chunk_list_reference_last = NULL; + +#ifdef USE_FASTPATH_CHUNKS_LAST + if (!BLI_listbase_is_empty(&chunk_list_reference->chunk_refs)) { + const BChunkRef *cref = chunk_list_reference->chunk_refs.last; + while ((cref->prev != NULL) && + (cref != cref_match_first) && + (cref->link->data_len <= data_len - i_prev)) + { + BChunk *chunk_test = cref->link; + size_t offset = data_len - chunk_test->data_len; + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + data_len = offset; + chunk_list_reference_last = cref; + chunk_list_reference_skip_len += 1; + chunk_list_reference_skip_bytes += cref->link->data_len; + cref = cref->prev; + } + else { + break; + } + } + } + + /* End Fast-Path (last) + * -------------------- */ +#endif /* USE_FASTPATH_CHUNKS_LAST */ + + /* ----------------------------------------------------------------------- + * Check for aligned chunks + * + * This saves a lot of searching, so use simple heuristics to detect aligned arrays. + * (may need to tweak exact method). + */ + + bool use_aligned = false; + +#ifdef USE_ALIGN_CHUNKS_TEST + if (chunk_list->total_size == chunk_list_reference->total_size) { + /* if we're already a quarter aligned */ + if (data_len - i_prev <= chunk_list->total_size / 4) { + use_aligned = true; + } + else { + /* TODO, walk over chunks and check if some arbitrary amount align */ + } + } +#endif /* USE_ALIGN_CHUNKS_TEST */ + + /* End Aligned Chunk Case + * ----------------------- */ + + if (use_aligned) { + /* Copy matching chunks, creates using the same 'layout' as the reference */ + const BChunkRef *cref = cref_match_first ? cref_match_first->next : chunk_list_reference->chunk_refs.first; + while (i_prev != data_len) { + const size_t i = i_prev + cref->link->data_len; + BLI_assert(i != i_prev); + + if ((cref != chunk_list_reference_last) && + bchunk_data_compare(cref->link, data, data_len, i_prev)) + { + bchunk_list_append(info, bs_mem, chunk_list, cref->link); + ASSERT_CHUNKLIST_SIZE(chunk_list, i); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + else { + bchunk_list_append_data(info, bs_mem, chunk_list, &data[i_prev], i - i_prev); + ASSERT_CHUNKLIST_SIZE(chunk_list, i); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + + cref = cref->next; + + i_prev = i; + } + } + else if ((data_len - i_prev >= info->chunk_byte_size) && + (chunk_list_reference->chunk_refs_len >= chunk_list_reference_skip_len) && + (chunk_list_reference->chunk_refs.first != NULL)) + { + + /* -------------------------------------------------------------------- + * Non-Aligned Chunk De-Duplication */ + + /* only create a table if we have at least one chunk to search + * otherwise just make a new one. + * + * Support re-arranged chunks */ + +#ifdef USE_HASH_TABLE_ACCUMULATE + size_t i_table_start = i_prev; + const size_t table_hash_array_len = (data_len - i_prev) / info->chunk_stride; + hash_key *table_hash_array = MEM_mallocN(sizeof(*table_hash_array) * table_hash_array_len, __func__); + hash_array_from_data(info, &data[i_prev], data_len - i_prev, table_hash_array); + + hash_accum(table_hash_array, table_hash_array_len, info->accum_steps); +#else + /* dummy vars */ + uint i_table_start = 0; + hash_key *table_hash_array = NULL; +#endif + + const uint chunk_list_reference_remaining_len = + (chunk_list_reference->chunk_refs_len - chunk_list_reference_skip_len) + 1; + BTableRef *table_ref_stack = MEM_mallocN(chunk_list_reference_remaining_len * sizeof(BTableRef), __func__); + uint table_ref_stack_n = 0; + + const size_t table_len = chunk_list_reference_remaining_len * BCHUNK_HASH_TABLE_MUL; + BTableRef **table = MEM_callocN(table_len * sizeof(*table), __func__); + + /* table_make - inline + * include one matching chunk, to allow for repeating values */ + { +#ifdef USE_HASH_TABLE_ACCUMULATE + const size_t hash_store_len = info->accum_read_ahead_len; + hash_key *hash_store = MEM_mallocN(sizeof(hash_key) * hash_store_len, __func__); +#endif + + const BChunkRef *cref; + size_t chunk_list_reference_bytes_remaining = + chunk_list_reference->total_size - chunk_list_reference_skip_bytes; + + if (cref_match_first) { + cref = cref_match_first; + chunk_list_reference_bytes_remaining += cref->link->data_len; + } + else { + cref = chunk_list_reference->chunk_refs.first; + } + +#ifdef USE_PARANOID_CHECKS + { + size_t test_bytes_len = 0; + const BChunkRef *cr = cref; + while (cr != chunk_list_reference_last) { + test_bytes_len += cr->link->data_len; + cr = cr->next; + } + BLI_assert(test_bytes_len == chunk_list_reference_bytes_remaining); + } +#endif + + while ((cref != chunk_list_reference_last) && + (chunk_list_reference_bytes_remaining >= info->accum_read_ahead_bytes)) + { + hash_key key = key_from_chunk_ref(info, cref + +#ifdef USE_HASH_TABLE_ACCUMULATE + , hash_store, hash_store_len +#endif + ); + size_t key_index = (size_t)(key % (hash_key)table_len); + BTableRef *tref_prev = table[key_index]; + BLI_assert(table_ref_stack_n < chunk_list_reference_remaining_len); + BTableRef *tref = &table_ref_stack[table_ref_stack_n++]; + tref->cref = cref; + tref->next = tref_prev; + table[key_index] = tref; + + chunk_list_reference_bytes_remaining -= cref->link->data_len; + cref = cref->next; + } + + BLI_assert(table_ref_stack_n <= chunk_list_reference_remaining_len); + +#ifdef USE_HASH_TABLE_ACCUMULATE + MEM_freeN(hash_store); +#endif + } + /* done making the table */ + + BLI_assert(i_prev <= data_len); + for (size_t i = i_prev; i < data_len; ) { + /* Assumes exiting chunk isnt a match! */ + + const BChunkRef *cref_found = table_lookup( + info, + table, table_len, i_table_start, + data, data_len, i, table_hash_array); + if (cref_found != NULL) { + BLI_assert(i < data_len); + if (i != i_prev) { + size_t i_step = MIN2(i_prev + info->chunk_byte_size, data_len); + BLI_assert(i_step <= data_len); + + while (i_prev != i) { + i_step = MIN2(i_step, i); + const ubyte *data_slice = &data[i_prev]; + const size_t data_slice_len = i_step - i_prev; + /* First add all previous chunks! */ + i_prev += data_slice_len; + bchunk_list_append_data(info, bs_mem, chunk_list, data_slice, data_slice_len); + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + i_step += info->chunk_byte_size; + } + } + + /* now add the reference chunk */ + { + BChunk *chunk_found = cref_found->link; + i += chunk_found->data_len; + bchunk_list_append(info, bs_mem, chunk_list, chunk_found); + } + i_prev = i; + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + /* its likely that the next chunk in the list will be a match, so check it! */ + while ((cref_found->next != NULL) && + (cref_found->next != chunk_list_reference_last)) + { + cref_found = cref_found->next; + BChunk *chunk_found = cref_found->link; + + if (bchunk_data_compare(chunk_found, data, data_len, i_prev)) { + /* may be useful to remove table data, assuming we dont have repeating memory + * where it would be useful to re-use chunks. */ + i += chunk_found->data_len; + bchunk_list_append(info, bs_mem, chunk_list, chunk_found); + /* chunk_found may be freed! */ + i_prev = i; + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + else { + break; + } + } + } + else { + i = i + info->chunk_stride; + } + } + +#ifdef USE_HASH_TABLE_ACCUMULATE + MEM_freeN(table_hash_array); +#endif + MEM_freeN(table); + MEM_freeN(table_ref_stack); + + /* End Table Lookup + * ---------------- */ + } + + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + /* ----------------------------------------------------------------------- + * No Duplicates to copy, write new chunks + * + * Trailing chunks, no matches found in table lookup above. + * Write all new data. */ + BLI_assert(i_prev <= data_len); + while (i_prev != data_len) { + size_t i = i_prev + info->chunk_byte_size; + i = MIN2(i, data_len); + BLI_assert(i != i_prev); + bchunk_list_append_data(info, bs_mem, chunk_list, &data[i_prev], i - i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + i_prev = i; + } + + BLI_assert(i_prev == data_len); + +#ifdef USE_FASTPATH_CHUNKS_LAST + if (chunk_list_reference_last != NULL) { + /* write chunk_list_reference_last since it hasn't been written yet */ + const BChunkRef *cref = chunk_list_reference_last; + while (cref != NULL) { + BChunk *chunk = cref->link; + // BLI_assert(bchunk_data_compare(chunk, data, data_len, i_prev)); + i_prev += chunk->data_len; + /* use simple since we assume the references chunks have already been sized correctly. */ + bchunk_list_append_only(bs_mem, chunk_list, chunk); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + cref = cref->next; + } + } +#endif + +#undef data_len_original + + BLI_assert(i_prev == data_len_original); + + /* check we're the correct size and that we didn't accidentally modify the reference */ + ASSERT_CHUNKLIST_SIZE(chunk_list, data_len_original); + ASSERT_CHUNKLIST_SIZE(chunk_list_reference, chunk_list_reference->total_size); + + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + return chunk_list; +} +/* end private API */ + +/** \} */ + + +/** \name Main Array Storage API + * \{ */ + + +/** + * Create a new array store, which can store any number of arrays + * as long as their stride matches. + * + * \param stride: ``sizeof()`` each element, + * + * \note while a stride of ``1`` will always work, + * its less efficient since duplicate chunks of memory will be searched + * at positions unaligned with the array data. + * + * \param chunk_count: Number of elements to split each chunk into. + * - A small value increases the ability to de-duplicate chunks, + * but adds overhead by increasing the number of chunks to look-up when searching for duplicates, + * as well as some overhead constructing the original array again, with more calls to ``memcpy``. + * - Larger values reduce the *book keeping* overhead, + * but increase the chance a small, isolated change will cause a larger amount of data to be duplicated. + * + * \return A new array store, to be freed with #BLI_array_store_destroy. + */ +BArrayStore *BLI_array_store_create( + uint stride, + uint chunk_count) +{ + BArrayStore *bs = MEM_callocN(sizeof(BArrayStore), __func__); + + bs->info.chunk_stride = stride; + bs->info.chunk_count = chunk_count; + + bs->info.chunk_byte_size = chunk_count * stride; +#ifdef USE_MERGE_CHUNKS + bs->info.chunk_byte_size_min = MAX2(1u, chunk_count / BCHUNK_SIZE_MIN_DIV) * stride; +#endif + +#ifdef USE_HASH_TABLE_ACCUMULATE + bs->info.accum_steps = BCHUNK_HASH_TABLE_ACCUMULATE_STEPS - 1; + /* Triangle number, identifying now much read-ahead we need: + * https://en.wikipedia.org/wiki/Triangular_number (+ 1) */ + bs->info.accum_read_ahead_len = (uint)((((bs->info.accum_steps * (bs->info.accum_steps + 1))) / 2) + 1); + bs->info.accum_read_ahead_bytes = bs->info.accum_read_ahead_len * stride; +#else + bs->info.accum_read_ahead_bytes = BCHUNK_HASH_LEN * stride; +#endif + + bs->memory.chunk_list = BLI_mempool_create(sizeof(BChunkList), 0, 512, BLI_MEMPOOL_NOP); + bs->memory.chunk_ref = BLI_mempool_create(sizeof(BChunkRef), 0, 512, BLI_MEMPOOL_NOP); + /* allow iteration to simplify freeing, otherwise its not needed + * (we could loop over all states as an alternative). */ + bs->memory.chunk = BLI_mempool_create(sizeof(BChunk), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + + return bs; +} + +static void array_store_free_data(BArrayStore *bs) +{ + /* free chunk data */ + { + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + BLI_assert(chunk->users > 0); + MEM_freeN((void *)chunk->data); + } + } + + /* free states */ + for (BArrayState *state = bs->states.first, *state_next; state; state = state_next) { + state_next = state->next; + MEM_freeN(state); + } +} + +/** + * Free the #BArrayStore, including all states and chunks. + */ +void BLI_array_store_destroy( + BArrayStore *bs) +{ + array_store_free_data(bs); + + BLI_mempool_destroy(bs->memory.chunk_list); + BLI_mempool_destroy(bs->memory.chunk_ref); + BLI_mempool_destroy(bs->memory.chunk); + + MEM_freeN(bs); +} + +/** + * Clear all contents, allowing reuse of \a bs. + */ +void BLI_array_store_clear( + BArrayStore *bs) +{ + array_store_free_data(bs); + + BLI_listbase_clear(&bs->states); + + BLI_mempool_clear(bs->memory.chunk_list); + BLI_mempool_clear(bs->memory.chunk_ref); + BLI_mempool_clear(bs->memory.chunk); +} + +/** \name BArrayStore Statistics + * \{ */ + +/** + * \return the total amount of memory that would be used by getting the arrays for all states. + */ +size_t BLI_array_store_calc_size_expanded_get( + const BArrayStore *bs) +{ + size_t size_accum = 0; + for (const BArrayState *state = bs->states.first; state; state = state->next) { + size_accum += state->chunk_list->total_size; + } + return size_accum; +} + +/** + * \return the amount of memory used by all #BChunk.data + * (duplicate chunks are only counted once). + */ +size_t BLI_array_store_calc_size_compacted_get( + const BArrayStore *bs) +{ + size_t size_total = 0; + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + BLI_assert(chunk->users > 0); + size_total += (size_t)chunk->data_len; + } + return size_total; +} + +/** \} */ + + +/** \name BArrayState Access + * \{ */ + +/** + * + * \param data: Data used to create + * \param state_reference: The state to use as a reference when adding the new state, + * typically this is the previous state, + * however it can be any previously created state from this \a bs. + * + * \return The new state, which is used by the caller as a handle to get back the contents of \a data. + * This may be removed using #BLI_array_store_state_remove, + * otherwise it will be removed with #BLI_array_store_destroy. + */ +BArrayState *BLI_array_store_state_add( + BArrayStore *bs, + const void *data, const size_t data_len, + const BArrayState *state_reference) +{ + /* ensure we're aligned to the stride */ + BLI_assert((data_len % bs->info.chunk_stride) == 0); + +#ifdef USE_PARANOID_CHECKS + if (state_reference) { + BLI_assert(BLI_findindex(&bs->states, state_reference) != -1); + } +#endif + + BChunkList *chunk_list; + if (state_reference) { + chunk_list = bchunk_list_from_data_merge( + &bs->info, &bs->memory, + (const ubyte *)data, data_len, + /* re-use reference chunks */ + state_reference->chunk_list); + } + else { + chunk_list = bchunk_list_new(&bs->memory, data_len); + bchunk_list_fill_from_array( + &bs->info, &bs->memory, + chunk_list, + (const ubyte *)data, data_len); + } + + chunk_list->users += 1; + + BArrayState *state = MEM_callocN(sizeof(BArrayState), __func__); + state->chunk_list = chunk_list; + + BLI_addtail(&bs->states, state); + +#ifdef USE_PARANOID_CHECKS + { + size_t data_test_len; + void *data_test = BLI_array_store_state_data_get_alloc(state, &data_test_len); + BLI_assert(data_test_len == data_len); + BLI_assert(memcmp(data_test, data, data_len) == 0); + MEM_freeN(data_test); + } +#endif + + return state; +} + +/** + * Remove a state and free any unused #BChunk data. + * + * The states can be freed in any order. + */ +void BLI_array_store_state_remove( + BArrayStore *bs, + BArrayState *state) +{ +#ifdef USE_PARANOID_CHECKS + BLI_assert(BLI_findindex(&bs->states, state) != -1); +#endif + + bchunk_list_decref(&bs->memory, state->chunk_list); + BLI_remlink(&bs->states, state); + + MEM_freeN(state); +} + +/** + * \return the expanded size of the array, + * use this to know how much memory to allocate #BLI_array_store_state_data_get's argument. + */ +size_t BLI_array_store_state_size_get( + BArrayState *state) +{ + return state->chunk_list->total_size; +} + +/** + * Fill in existing allocated memory with the contents of \a state. + */ +void BLI_array_store_state_data_get( + BArrayState *state, + void *data) +{ +#ifdef USE_PARANOID_CHECKS + size_t data_test_len = 0; + for (BChunkRef *cref = state->chunk_list->chunk_refs.first; cref; cref = cref->next) { + data_test_len += cref->link->data_len; + } + BLI_assert(data_test_len == state->chunk_list->total_size); +#endif + + ubyte *data_step = (ubyte *)data; + for (BChunkRef *cref = state->chunk_list->chunk_refs.first; cref; cref = cref->next) { + BLI_assert(cref->link->users > 0); + memcpy(data_step, cref->link->data, cref->link->data_len); + data_step += cref->link->data_len; + } +} + +/** + * Allocate an array for \a state and return it. + */ +void *BLI_array_store_state_data_get_alloc( + BArrayState *state, + size_t *r_data_len) +{ + void *data = MEM_mallocN(state->chunk_list->total_size, __func__); + BLI_array_store_state_data_get(state, data); + *r_data_len = state->chunk_list->total_size; + return data; +} + +/** \} */ + + +/** \name Debigging API (for testing). + * \{ */ + +/* only for test validation */ +static size_t bchunk_list_size(const BChunkList *chunk_list) +{ + size_t total_size = 0; + for (BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + total_size += cref->link->data_len; + } + return total_size; +} + +bool BLI_array_store_is_valid( + BArrayStore *bs) +{ + bool ok = true; + + /* Check Length + * ------------ */ + + for (BArrayState *state = bs->states.first; state; state = state->next) { + BChunkList *chunk_list = state->chunk_list; + if (!(bchunk_list_size(chunk_list) == chunk_list->total_size)) { + return false; + } + } + + { + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + if (!(MEM_allocN_len(chunk->data) >= chunk->data_len)) { + return false; + } + } + } + + /* Check User Count & Lost References + * ---------------------------------- */ + { + GHashIterator gh_iter; + +#define GHASH_PTR_ADD_USER(gh, pt) \ + { \ + void **val; \ + if (BLI_ghash_ensure_p((gh), (pt), &val)) { \ + *((int *)val) += 1; \ + } \ + else { \ + *((int *)val) = 1; \ + } \ + } ((void)0) + + + /* count chunk_list's */ + int totrefs = 0; + GHash *chunk_list_map = BLI_ghash_ptr_new(__func__); + for (BArrayState *state = bs->states.first; state; state = state->next) { + GHASH_PTR_ADD_USER(chunk_list_map, state->chunk_list); + } + GHASH_ITER (gh_iter, chunk_list_map) { + const struct BChunkList *chunk_list = BLI_ghashIterator_getKey(&gh_iter); + const int users = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); + if (!(chunk_list->users == users)) { + ok = false; + goto user_finally; + } + } + if (!(BLI_mempool_count(bs->memory.chunk_list) == (int)BLI_ghash_size(chunk_list_map))) { + ok = false; + goto user_finally; + } + + /* count chunk's */ + GHash *chunk_map = BLI_ghash_ptr_new(__func__); + GHASH_ITER (gh_iter, chunk_list_map) { + const struct BChunkList *chunk_list = BLI_ghashIterator_getKey(&gh_iter); + for (const BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + GHASH_PTR_ADD_USER(chunk_map, cref->link); + totrefs += 1; + } + } + if (!(BLI_mempool_count(bs->memory.chunk) == (int)BLI_ghash_size(chunk_map))) { + ok = false; + goto user_finally; + } + if (!(BLI_mempool_count(bs->memory.chunk_ref) == totrefs)) { + ok = false; + goto user_finally; + } + + GHASH_ITER (gh_iter, chunk_map) { + const struct BChunk *chunk = BLI_ghashIterator_getKey(&gh_iter); + const int users = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); + if (!(chunk->users == users)) { + ok = false; + goto user_finally; + } + } + +#undef GHASH_PTR_ADD_USER + +user_finally: + BLI_ghash_free(chunk_list_map, NULL, NULL); + BLI_ghash_free(chunk_map, NULL, NULL); + } + + + return ok; + /* TODO, dangling pointer checks */ +} + +/** \} */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index 7b102c9283b..bb61f66e267 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -804,10 +804,10 @@ void BM_mesh_bm_to_me( BMEditSelection *selected; me->totselect = BLI_listbase_count(&(bm->selected)); - if (me->mselect) MEM_freeN(me->mselect); - - me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); - + MEM_SAFE_FREE(me->mselect); + if (me->totselect != 0) { + me->mselect = MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + } for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { if (selected->htype == BM_VERT) { diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 5a7788c0b62..f7e3622e53c 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -345,6 +345,36 @@ static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e) return NULL; } +/* return count of edges between e1 and e2 when going around bv CCW */ +static int count_ccw_edges_between(EdgeHalf *e1, EdgeHalf *e2) +{ + int cnt = 0; + EdgeHalf *e = e1; + + do { + if (e == e2) + break; + e = e->next; + cnt++; + } while (e != e1); + return cnt; +} + +/* Assume bme1 and bme2 both share some vert. Do they share a face? + * If they share a face then there is some loop around bme1 that is in a face + * where the next or previous edge in the face must be bme2. */ +static bool edges_face_connected_at_vert(BMEdge *bme1, BMEdge *bme2) +{ + BMLoop *l; + BMIter iter; + + BM_ITER_ELEM(l, &iter, bme1, BM_LOOPS_OF_EDGE) { + if (l->prev->e == bme2 || l->next->e == bme2) + return true; + } + return false; +} + /* Return a good representative face (for materials, etc.) for faces * created around/near BoundVert v. * Sometimes care about a second choice, if there is one. @@ -1557,7 +1587,7 @@ static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool constr if (construct) { v = add_new_bound_vert(bp->mem_arena, vm, co); v->efirst = v->elast = e; - e->leftv = v; + e->leftv = e->rightv = v; } else { adjust_bound_vert(e->leftv, co); @@ -1637,7 +1667,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf v->efirst = e->prev; v->elast = v->ebev = e; e->leftv = v; - e->prev->leftv = v; + e->prev->leftv = e->prev->rightv = v; } else { adjust_bound_vert(e->leftv, co); @@ -1648,7 +1678,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf v = add_new_bound_vert(mem_arena, vm, co); v->efirst = e->prev; v->elast = e; - e->leftv = v; + e->leftv = e->rightv = v; e->prev->rightv = v; } else { @@ -1661,7 +1691,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf if (construct) { v = add_new_bound_vert(mem_arena, vm, co); v->efirst = v->elast = e; - e->leftv = v; + e->leftv = e->rightv = v; } else { adjust_bound_vert(e->leftv, co); @@ -3237,6 +3267,11 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) if (!weld) create_mesh_bmvert(bm, vm, i, 0, k, bv->v); } + else if (n == 2 && !v->ebev && vm->mesh_kind != M_ADJ) { + /* case of one edge beveled and this is the v without ebev */ + /* want to copy the verts from other v, in reverse order */ + copy_mesh_vert(vm, i, 0, k, 1 - i, 0, ns - k); + } } } while ((v = v->next) != vm->boundstart); @@ -3305,6 +3340,219 @@ static float edge_face_angle(EdgeHalf *e) #define BM_BEVEL_EDGE_TAG_DISABLE(bme) BM_ELEM_API_FLAG_DISABLE( (bme), _FLAG_OVERLAP) #define BM_BEVEL_EDGE_TAG_TEST(bme) BM_ELEM_API_FLAG_TEST( (bme), _FLAG_OVERLAP) +/* Try to extend the bv->edges[] array beyond i by finding more successor edges. + * This is a possibly exponential-time search, but it is only exponential in the number + * of "internal faces" at a vertex -- i.e., faces that bridge between the edges that naturally + * form a manifold cap around bv. It is rare to have more than one of these, so unlikely + * that the exponential time case will be hit in practice. + * Returns the new index i' where bv->edges[i'] ends the best path found. + * The path will have the tags of all of its edges set. */ +static int bevel_edge_order_extend(BMesh *bm, BevVert *bv, int i) +{ + BMEdge *bme, *bme2, *nextbme; + BMLoop *l; + BMIter iter; + int j, tryj, bestj, nsucs, sucindex, k; + BMEdge **sucs = NULL; + BMEdge **save_path = NULL; + BLI_array_staticdeclare(sucs, 4); /* likely very few faces attached to same edge */ + BLI_array_staticdeclare(save_path, BM_DEFAULT_NGON_STACK_SIZE); + + bme = bv->edges[i].e; + /* fill sucs with all unmarked edges of bmes */ + BM_ITER_ELEM(l, &iter, bme, BM_LOOPS_OF_EDGE) { + bme2 = (l->v == bv->v) ? l->prev->e : l->next->e; + if (!BM_BEVEL_EDGE_TAG_TEST(bme2)) { + BLI_array_append(sucs, bme2); + } + } + nsucs = BLI_array_count(sucs); + + bestj = j = i; + for (sucindex = 0; sucindex < nsucs; sucindex++) { + nextbme = sucs[sucindex]; + BLI_assert(nextbme != NULL); + BLI_assert(!BM_BEVEL_EDGE_TAG_TEST(nextbme)); + BLI_assert(j + 1 < bv->edgecount); + bv->edges[j + 1].e = nextbme; + BM_BEVEL_EDGE_TAG_ENABLE(nextbme); + tryj = bevel_edge_order_extend(bm, bv, j + 1); + if (tryj > bestj || (tryj == bestj && edges_face_connected_at_vert(bv->edges[tryj].e, bv->edges[0].e))) { + bestj = tryj; + BLI_array_empty(save_path); + for (k = j + 1; k <= bestj; k++) { + BLI_array_append(save_path, bv->edges[k].e); + } + } + /* now reset to path only-going-to-j state */ + for (k = j + 1; k <= tryj; k++) { + BM_BEVEL_EDGE_TAG_DISABLE(bv->edges[k].e); + bv->edges[k].e = NULL; + } + } + /* at this point we should be back at invariant on entrance: path up to j */ + if (bestj > j) { + /* save_path should have from j + 1 to bestj inclusive edges to add to edges[] before returning */ + for (k = j + 1; k <= bestj; k++) { + BLI_assert(save_path[k - (j + 1)] != NULL); + bv->edges[k].e = save_path[k - (j + 1)]; + BM_BEVEL_EDGE_TAG_ENABLE(bv->edges[k].e); + } + } + BLI_array_free(sucs); + BLI_array_free(save_path); + return bestj; +} + +/* See if we have usual case for bevel edge order: + * there is an ordering such that all the faces are between + * successive edges and form a manifold "cap" at bv. + * If this is the case, set bv->edges to such an order + * and return true; else return unmark any partial path and return false. + * Assume the first edge is already in bv->edges[0].e and it is tagged. */ +#ifdef FASTER_FASTORDER +/* The alternative older code is O(n^2) where n = # of edges incident to bv->v. + * This implementation is O(n * m) where m = average number of faces attached to an edge incident to bv->v, + * which is almost certainly a small constant except in very strange cases. But this code produces different + * choices of ordering than the legacy system, leading to differences in vertex orders etc. in user models, + * so for now will continue to use the legacy code. */ +static bool fast_bevel_edge_order(BevVert *bv) +{ + int j, k, nsucs; + BMEdge *bme, *bme2, *bmenext; + BMIter iter; + BMLoop *l; + + for (j = 1; j < bv->edgecount; j++) { + bme = bv->edges[j - 1].e; + bmenext = NULL; + nsucs = 0; + BM_ITER_ELEM(l, &iter, bme, BM_LOOPS_OF_EDGE) { + bme2 = (l->v == bv->v) ? l->prev->e : l->next->e; + if (!BM_BEVEL_EDGE_TAG_TEST(bme2)) { + nsucs++; + if (bmenext == NULL) + bmenext = bme2; + } + } + if (nsucs == 0 || (nsucs == 2 && j != 1) || nsucs > 2 || + (j == bv->edgecount - 1 && !edges_face_connected_at_vert(bmenext, bv->edges[0].e))) + { + for (k = 1; k < j; k++) { + BM_BEVEL_EDGE_TAG_DISABLE(bv->edges[k].e); + bv->edges[k].e = NULL; + } + return false; + } + bv->edges[j].e = bmenext; + BM_BEVEL_EDGE_TAG_ENABLE(bmenext); + } + return true; +} +#else +static bool fast_bevel_edge_order(BevVert *bv) +{ + BMEdge *bme, *bme2, *first_suc; + BMIter iter, iter2; + BMFace *f; + EdgeHalf *e; + int i, k, ntot, num_shared_face; + + ntot = bv->edgecount; + + /* add edges to bv->edges in order that keeps adjacent edges sharing + * a unique face, if possible */ + e = &bv->edges[0]; + bme = e->e; + if (!bme->l) + return false; + for (i = 1; i < ntot; i++) { + /* find an unflagged edge bme2 that shares a face f with previous bme */ + num_shared_face = 0; + first_suc = NULL; /* keep track of first successor to match legacy behavior */ + BM_ITER_ELEM (bme2, &iter, bv->v, BM_EDGES_OF_VERT) { + if (BM_BEVEL_EDGE_TAG_TEST(bme2)) + continue; + BM_ITER_ELEM (f, &iter2, bme2, BM_FACES_OF_EDGE) { + if (BM_face_edge_share_loop(f, bme)) { + num_shared_face++; + if (first_suc == NULL) + first_suc = bme2; + } + } + if (num_shared_face >= 3) + break; + } + if (num_shared_face == 1 || (i == 1 && num_shared_face == 2)) { + e = &bv->edges[i]; + e->e = bme = first_suc; + BM_BEVEL_EDGE_TAG_ENABLE(bme); + } + else { + for (k = 1; k < i; k++) { + BM_BEVEL_EDGE_TAG_DISABLE(bv->edges[k].e); + bv->edges[k].e = NULL; + } + return false; + } + } + return true; +} +#endif + +/* Fill in bv->edges with a good ordering of non-wire edges around bv->v. + * Use only edges where BM_BEVEL_EDGE_TAG is disabled so far + * (if edge beveling, others are wire). + * first_bme is a good edge to start with.*/ +static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme) +{ + BMEdge *bme, *bme2; + BMIter iter; + BMFace *f; + EdgeHalf *e; + EdgeHalf *e2; + BMLoop *l; + int i, ntot; + + ntot = bv->edgecount; + i = 0; + for (;;) { + BLI_assert(first_bme != NULL); + bv->edges[i].e = first_bme; + BM_BEVEL_EDGE_TAG_ENABLE(first_bme); + if (fast_bevel_edge_order(bv)) + break; + i = bevel_edge_order_extend(bm, bv, i); + i++; + if (i >= bv->edgecount) + break; + /* Not done yet: find a new first_bme */ + first_bme = NULL; + BM_ITER_ELEM(bme, &iter, bv->v, BM_EDGES_OF_VERT) { + if (BM_BEVEL_EDGE_TAG_TEST(bme)) + continue; + if (!first_bme) + first_bme = bme; + if (BM_edge_face_count(bme) == 1) { + first_bme = bme; + break; + } + } + } + /* now fill in the faces ... */ + for (i = 0; i < ntot; i++) { + e = &bv->edges[i]; + e2 = (i == bv->edgecount - 1) ? &bv->edges[0] : &bv->edges[i + 1]; + bme = e->e; + bme2 = e2->e; + BM_ITER_ELEM(l, &iter, bme, BM_LOOPS_OF_EDGE) { + f = l->f; + if ((l->prev->e == bme2 || l->next->e == bme2) && !e->fnext && !e2->fprev) + e->fnext = e2->fprev = f; + } + } +} + /* * Construction around the vertex */ @@ -3312,13 +3560,12 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) { BMEdge *bme; BevVert *bv; - BMEdge *bme2, *unflagged_bme, *first_bme; - BMFace *f; + BMEdge *first_bme; BMVert *v1, *v2; - BMIter iter, iter2; + BMIter iter; EdgeHalf *e; float weight, z; - int i, found_shared_face, ccw_test_sum; + int i, ccw_test_sum; int nsel = 0; int ntot = 0; int nwire = 0; @@ -3398,47 +3645,12 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) } BLI_ghash_insert(bp->vert_hash, v, bv); - /* add edges to bv->edges in order that keeps adjacent edges sharing - * a face, if possible */ - i = 0; + find_bevel_edge_order(bm, bv, first_bme); - bme = first_bme; - BM_BEVEL_EDGE_TAG_ENABLE(bme); - e = &bv->edges[0]; - e->e = bme; + /* fill in other attributes of EdgeHalfs */ for (i = 0; i < ntot; i++) { - if (i > 0) { - /* find an unflagged edge bme2 that shares a face f with previous bme */ - found_shared_face = 0; - unflagged_bme = NULL; - BM_ITER_ELEM (bme2, &iter, v, BM_EDGES_OF_VERT) { - if (BM_BEVEL_EDGE_TAG_TEST(bme2)) - continue; - if (!unflagged_bme) - unflagged_bme = bme2; - if (!bme->l) - continue; - BM_ITER_ELEM (f, &iter2, bme2, BM_FACES_OF_EDGE) { - if (BM_face_edge_share_loop(f, bme)) { - found_shared_face = 1; - break; - } - } - if (found_shared_face) - break; - } - e = &bv->edges[i]; - if (found_shared_face) { - e->e = bme2; - e->fprev = f; - bv->edges[i - 1].fnext = f; - } - else { - e->e = unflagged_bme; - } - } + e = &bv->edges[i]; bme = e->e; - BM_BEVEL_EDGE_TAG_ENABLE(bme); if (BM_elem_flag_test(bme, BM_ELEM_TAG) && !bp->vertex_only) { e->is_bev = true; e->seg = bp->seg; @@ -3449,16 +3661,6 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) } e->is_rev = (bme->v2 == v); } - /* find wrap-around shared face */ - BM_ITER_ELEM (f, &iter2, bme, BM_FACES_OF_EDGE) { - if (bv->edges[0].e->l && BM_face_edge_share_loop(f, bv->edges[0].e)) { - if (bv->edges[0].fnext == f) - continue; /* if two shared faces, want the other one now */ - bv->edges[ntot - 1].fnext = f; - bv->edges[0].fprev = f; - break; - } - } /* now done with tag flag */ BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) { @@ -3586,6 +3788,7 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) VMesh *vm; int i, k, n; bool do_rebuild = false; + bool go_ccw, corner3special; BMVert *bmv; BMEdge *bme, *bme_new, *bme_prev; BMFace *f_new; @@ -3600,47 +3803,88 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { lprev = l->prev; bv = find_bevvert(bp, l->v); + vm = bv->vmesh; e = find_edge_half(bv, l->e); bme = e->e; eprev = find_edge_half(bv, lprev->e); BLI_assert(e != NULL && eprev != NULL); - vstart = eprev->leftv; - if (e->is_bev) - vend = e->rightv; - else + + /* which direction around our vertex do we travel to match orientation of f? */ + if (e->prev == eprev) { + if (eprev->prev == e) { + /* valence 2 vertex: use f is one of e->fnext or e->fprev to break tie */ + go_ccw = (e->fnext != f); + } + else { + go_ccw = true; /* going ccw around bv to trace this corner */ + } + } + else if (eprev->prev == e) { + go_ccw = false; /* going cw around bv to trace this corner */ + } + else { + /* edges in face are non-contiguous in our ordering around bv. + * Which way should we go when going from eprev to e? */ + if (count_ccw_edges_between(eprev, e) < count_ccw_edges_between(e, eprev)) { + /* go counterclockewise from eprev to e */ + go_ccw = true; + } + else { + /* go clockwise from eprev to e */ + go_ccw = false; + } + } + if (go_ccw) { + vstart = eprev->rightv; vend = e->leftv; + } + else { + vstart = eprev->leftv; + vend = e->rightv; + } + BLI_assert(vstart != NULL && vend != NULL); v = vstart; - vm = bv->vmesh; BLI_array_append(vv, v->nv.v); BLI_array_append(ee, bme); + /* check for special case: multisegment 3rd face opposite a beveled edge with no vmesh */ + corner3special = (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev); while (v != vend) { - if (vm->mesh_kind == M_NONE && v->ebev && v->ebev->seg > 1 && v->ebev != e && v->ebev != eprev) { - /* case of 3rd face opposite a beveled edge, with no vmesh */ - i = v->index; - e = v->ebev; - for (k = 1; k < e->seg; k++) { - bmv = mesh_vert(vm, i, 0, k)->v; - BLI_array_append(vv, bmv); - BLI_array_append(ee, bme); - /* may want to merge UVs of these later */ - if (!e->is_seam) - BLI_array_append(vv_fix, bmv); + if (go_ccw) { + if (vm->seg > 1) { + if (vm->mesh_kind == M_ADJ || bp->vertex_only || corner3special) { + i = v->index; + for (k = 1; k < vm->seg; k++) { + bmv = mesh_vert(vm, i, 0, k)->v; + BLI_array_append(vv, bmv); + BLI_array_append(ee, bme); /* TODO: maybe better edge here */ + if (corner3special && v->ebev && !v->ebev->is_seam) + BLI_array_append(vv_fix, bmv); + } + } } + v = v->next; } - else if ((vm->mesh_kind == M_ADJ || bp->vertex_only) && vm->seg > 1 && !e->is_bev && !eprev->is_bev) { - BLI_assert(v->prev == vend); - i = vend->index; - for (k = vm->seg - 1; k > 0; k--) { - bmv = mesh_vert(vm, i, 0, k)->v; - BLI_array_append(vv, bmv); - BLI_array_append(ee, bme); + else { + /* going cw */ + if (vm->seg > 1) { + if (vm->mesh_kind == M_ADJ || bp->vertex_only || + (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev)) + { + i = v->prev->index; + for (k = vm->seg - 1; k > 0; k--) { + bmv = mesh_vert(vm, i, 0, k)->v; + BLI_array_append(vv, bmv); + BLI_array_append(ee, bme); + if (corner3special && v->ebev && !v->ebev->is_seam) + BLI_array_append(vv_fix, bmv); + } + } } + v = v->prev; } - v = v->prev; BLI_array_append(vv, v->nv.v); BLI_array_append(ee, bme); } - do_rebuild = true; } else { diff --git a/source/blender/collada/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp index 36ab85b9b5b..4f5cf83f5ca 100644 --- a/source/blender/collada/ArmatureExporter.cpp +++ b/source/blender/collada/ArmatureExporter.cpp @@ -67,12 +67,19 @@ void ArmatureExporter::add_armature_bones(Object *ob_arm, Scene *sce, std::list<Object *>& child_objects) { // write bone nodes + + bArmature * armature = (bArmature *)ob_arm->data; + ED_armature_to_edit(armature); + bArmature *arm = (bArmature *)ob_arm->data; for (Bone *bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) { // start from root bones if (!bone->parent) add_bone_node(bone, ob_arm, sce, se, child_objects); } + + ED_armature_from_edit(armature); + ED_armature_edit_free(armature); } void ArmatureExporter::write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone) @@ -167,12 +174,30 @@ void ArmatureExporter::add_bone_node(Bone *bone, Object *ob_arm, Scene *sce, node.setNodeName(node_name); node.setNodeSid(node_sid); -#if 0 - if (BLI_listbase_is_empty(&bone->childbase) || BLI_listbase_count_ex(&bone->childbase, 2) == 2) { - add_blender_leaf_bone( bone, ob_arm, node); + if (this->export_settings->use_blender_profile) + { + if (bone->parent) { + if (bone->flag & BONE_CONNECTED) { + node.addExtraTechniqueParameter("blender", "connect", true); + } + } + std::string layers = BoneExtended::get_bone_layers(bone->layer); + node.addExtraTechniqueParameter("blender", "layer", layers); + + bArmature *armature = (bArmature *)ob_arm->data; + EditBone *ebone = bc_get_edit_bone(armature, bone->name); + if (ebone && ebone->roll != 0) + { + node.addExtraTechniqueParameter("blender", "roll", ebone->roll); + } + if (bc_is_leaf_bone(bone)) + { + node.addExtraTechniqueParameter("blender", "tip_x", bone->arm_tail[0] - bone->arm_head[0]); + node.addExtraTechniqueParameter("blender", "tip_y", bone->arm_tail[1] - bone->arm_head[1]); + node.addExtraTechniqueParameter("blender", "tip_z", bone->arm_tail[2] - bone->arm_head[2]); + } } - else { -#endif + node.start(); add_bone_transform(ob_arm, bone, node); @@ -227,25 +252,6 @@ void ArmatureExporter::add_bone_node(Bone *bone, Object *ob_arm, Scene *sce, } } -//#if 1 -void ArmatureExporter::add_blender_leaf_bone(Bone *bone, Object *ob_arm, COLLADASW::Node& node) -{ - node.start(); - - add_bone_transform(ob_arm, bone, node); - - node.addExtraTechniqueParameter("blender", "tip_x", bone->tail[0]); - node.addExtraTechniqueParameter("blender", "tip_y", bone->tail[1]); - node.addExtraTechniqueParameter("blender", "tip_z", bone->tail[2]); - - /*for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - add_bone_node(child, ob_arm, sce, se, child_objects); - }*/ - node.end(); - -} -//#endif - void ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node) { //bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name); diff --git a/source/blender/collada/ArmatureExporter.h b/source/blender/collada/ArmatureExporter.h index 931cc5d2988..883a6aca847 100644 --- a/source/blender/collada/ArmatureExporter.h +++ b/source/blender/collada/ArmatureExporter.h @@ -92,8 +92,6 @@ private: void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node); - void add_blender_leaf_bone(Bone *bone, Object *ob_arm, COLLADASW::Node& node); - std::string get_controller_id(Object *ob_arm, Object *ob); void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone); diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp index fd08e1ebfab..fca9b9ffa55 100644 --- a/source/blender/collada/ArmatureImporter.cpp +++ b/source/blender/collada/ArmatureImporter.cpp @@ -50,19 +50,6 @@ static const char *bc_get_joint_name(T *node) return id.size() ? id.c_str() : node->getOriginalId().c_str(); } -static EditBone *get_edit_bone(bArmature * armature, char *name) { - EditBone *eBone; - - for (eBone = (EditBone *)armature->edbo->first; eBone; eBone = eBone->next) { - if (STREQ(name, eBone->name)) - return eBone; - } - - return NULL; - -} - - ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce, const ImportSettings *import_settings) : import_settings(import_settings), @@ -110,7 +97,7 @@ JointData *ArmatureImporter::get_joint_data(COLLADAFW::Node *node); #endif int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBone *parent, int totchild, - float parent_mat[4][4], bArmature *arm) + float parent_mat[4][4], bArmature *arm, std::vector<std::string> &layer_labels) { float mat[4][4]; float joint_inv_bind_mat[4][4]; @@ -157,22 +144,41 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon if (parent) bone->parent = parent; float loc[3], size[3], rot[3][3]; - float angle; - float vec[3] = {0.0f, 0.5f, 0.0f}; - mat4_to_loc_rot_size(loc, rot, size, mat); - //copy_m3_m4(bonemat,mat); - mat3_to_vec_roll(rot, vec, &angle); - - bone->roll = angle; - // set head - copy_v3_v3(bone->head, mat[3]); - // set tail, don't set it to head because 0-length bones are not allowed - add_v3_v3v3(bone->tail, bone->head, vec); + BoneExtended &be = add_bone_extended(bone, node, layer_labels); + int layer = be.get_bone_layers(); + if (layer) bone->layer = layer; + arm->layer |= layer; // ensure that all populated bone layers are visible after import + + float *tail = be.get_tail(); + int use_connect = be.get_use_connect(); + + switch (use_connect) { + case 1: bone->flag |= BONE_CONNECTED; + break; + case 0: bone->flag &= ~BONE_CONNECTED; + case -1: break; // not defined + } + + if (be.has_roll()) { + bone->roll = be.get_roll(); + } + else { + float angle; + mat4_to_loc_rot_size(loc, rot, size, mat); + mat3_to_vec_roll(rot, NULL, &angle); + } + + copy_v3_v3(bone->head, mat[3]); + add_v3_v3v3(bone->tail, bone->head, tail); //tail must be non zero /* find smallest bone length in armature (used later for leaf bone length) */ if (parent) { + if (use_connect == 1) { + copy_v3_v3(parent->tail, bone->head); + } + /* guess reasonable leaf bone length */ float length = len_v3v3(parent->head, bone->head); if ((length < leaf_bone_length || totbone == 0) && length > MINIMUM_BONE_LENGTH) { @@ -182,11 +188,8 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon COLLADAFW::NodePointerArray& children = node->getChildNodes(); - BoneExtended &be = add_bone_extended(bone, node); - be.set_leaf_bone(true); - for (unsigned int i = 0; i < children.getCount(); i++) { - int cl = create_bone(skin, children[i], bone, children.getCount(), mat, arm); + int cl = create_bone(skin, children[i], bone, children.getCount(), mat, arm, layer_labels); if (cl > chain_length) chain_length = cl; } @@ -209,22 +212,23 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon **/ void ArmatureImporter::fix_leaf_bones(bArmature *armature, Bone *bone) { - /* armature has no bones */ if (bone == NULL) return; - BoneExtended *be = extended_bones[bone->name]; - if (be != NULL && be->is_leaf_bone() ) { - /* Collada only knows Joints, Here we guess a reasonable leaf bone length */ - float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0 : leaf_bone_length; + if (bc_is_leaf_bone(bone)) { + + BoneExtended *be = extended_bones[bone->name]; + if (be == NULL || !be->has_tail()) { - EditBone *ebone = get_edit_bone(armature, bone->name); - float vec[3]; + /* Collada only knows Joints, Here we guess a reasonable leaf bone length */ + float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0 : leaf_bone_length; + + EditBone *ebone = bc_get_edit_bone(armature, bone->name); + float vec[3]; - if (this->import_settings->fix_orientation) { if (ebone->parent != NULL) { EditBone *parent = ebone->parent; - sub_v3_v3v3(vec, ebone->head, parent->tail); + sub_v3_v3v3(vec, ebone->head, parent->head); if (len_squared_v3(vec) < MINIMUM_BONE_LENGTH) { sub_v3_v3v3(vec, parent->tail, parent->head); @@ -234,19 +238,31 @@ void ArmatureImporter::fix_leaf_bones(bArmature *armature, Bone *bone) vec[2] = 0.1f; sub_v3_v3v3(vec, ebone->tail, ebone->head); } - } - else { - sub_v3_v3v3(vec, ebone->tail, ebone->head); - } - normalize_v3_v3(vec, vec); - mul_v3_fl(vec, leaf_length); - add_v3_v3v3(ebone->tail, ebone->head, vec); + normalize_v3_v3(vec, vec); + mul_v3_fl(vec, leaf_length); + add_v3_v3v3(ebone->tail, ebone->head, vec); + } } for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { fix_leaf_bones(armature, child); } +} + +void ArmatureImporter::fix_parent_connect(bArmature *armature, Bone *bone) +{ + /* armature has no bones */ + if (bone == NULL) + return; + + if (bone->parent && bone->flag & BONE_CONNECTED) { + copy_v3_v3(bone->parent->tail, bone->head); + } + + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + fix_parent_connect(armature, child); + } } @@ -281,8 +297,8 @@ void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone BoneExtended *pbe = extended_bones[parentbone->name]; if (dominant_child != NULL) { /* Found a valid chain. Now connect current bone with that chain.*/ - EditBone *pebone = get_edit_bone(armature, parentbone->name); - EditBone *cebone = get_edit_bone(armature, dominant_child->get_name()); + EditBone *pebone = bc_get_edit_bone(armature, parentbone->name); + EditBone *cebone = bc_get_edit_bone(armature, dominant_child->get_name()); if (pebone && !(cebone->flag & BONE_CONNECTED)) { float vec[3]; @@ -421,6 +437,7 @@ ArmatureJoints& ArmatureImporter::get_armature_joints(Object *ob_arm) void ArmatureImporter::create_armature_bones( ) { std::vector<COLLADAFW::Node *>::iterator ri; + std::vector<std::string> layer_labels; leaf_bone_length = FLT_MAX; //if there is an armature created for root_joint next root_joint @@ -445,8 +462,9 @@ void ArmatureImporter::create_armature_bones( ) clear_extended_boneset(); ED_armature_to_edit(armature); + armature->layer = 0; // layer is set according to imported bone set in create_bone() - create_bone(NULL, *ri , NULL, (*ri)->getChildNodes().getCount(), NULL, armature); + create_bone(NULL, *ri , NULL, (*ri)->getChildNodes().getCount(), NULL, armature, layer_labels); /* exit armature edit mode to populate the Armature object */ ED_armature_from_edit(armature); @@ -455,14 +473,23 @@ void ArmatureImporter::create_armature_bones( ) /* and step back to edit mode to fix the leaf nodes */ ED_armature_to_edit(armature); - connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - fix_leaf_bones(armature, (Bone *)armature->bonebase.first); + if (this->import_settings->fix_orientation || this->import_settings->find_chains) { + + if (this->import_settings->find_chains) + connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - // exit armature edit mode - unskinned_armature_map[(*ri)->getUniqueId()] = ob_arm; + if (this->import_settings->fix_orientation) + fix_leaf_bones(armature, (Bone *)armature->bonebase.first); + + // exit armature edit mode + unskinned_armature_map[(*ri)->getUniqueId()] = ob_arm; + } + + fix_parent_connect(armature, (Bone *)armature->bonebase.first); ED_armature_from_edit(armature); ED_armature_edit_free(armature); + DAG_id_tag_update(&ob_arm->id, OB_RECALC_OB | OB_RECALC_DATA); } } @@ -513,6 +540,7 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) SkinInfo *a = &skin; Object *shared = NULL; std::vector<COLLADAFW::Node *> skin_root_joints; + std::vector<std::string> layer_labels; std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { @@ -578,7 +606,7 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) // since root_joints may contain joints for multiple controllers, we need to filter if (skin.uses_joint_or_descendant(*ri)) { - create_bone(&skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, armature); + create_bone(&skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, armature, layer_labels); if (joint_parent_map.find((*ri)->getUniqueId()) != joint_parent_map.end() && !skin.get_parent()) skin.set_parent(joint_parent_map[(*ri)->getUniqueId()]); @@ -594,8 +622,8 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) if (armature->bonebase.first) { /* Do this only if Armature has bones */ - connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - fix_leaf_bones(armature, (Bone *)armature->bonebase.first); + //connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); + //fix_leaf_bones(armature, (Bone *)armature->bonebase.first); } // exit armature edit mode ED_armature_from_edit(armature); @@ -894,70 +922,44 @@ bool ArmatureImporter::get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint) return found; } - -/** - * BoneExtended is a helper class needed for the Bone chain finder - * See ArmatureImporter::fix_leaf_bones() - * and ArmatureImporter::connect_bone_chains() - **/ - -BoneExtended::BoneExtended(EditBone *aBone) -{ - this->set_name(aBone->name); - this->chain_length = 0; - this->is_leaf = false; -} - -char *BoneExtended::get_name() -{ - return name; -} - -void BoneExtended::set_name(char *aName) -{ - BLI_strncpy(name, aName, MAXBONENAME); -} - -int BoneExtended::get_chain_length() -{ - return chain_length; -} - -void BoneExtended::set_chain_length(const int aLength) -{ - chain_length = aLength; -} - - -void BoneExtended::set_leaf_bone(bool state) -{ - is_leaf = state; -} - -bool BoneExtended::is_leaf_bone() -{ - return is_leaf; -} - -BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Node *node) +BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Node *node, std::vector<std::string> &layer_labels) { + BoneExtended *be = new BoneExtended(bone); + extended_bones[bone->name] = be; TagsMap::iterator etit; ExtraTags *et = 0; etit = uid_tags_map.find(node->getUniqueId().toAscii()); if (etit != uid_tags_map.end()) { - float x, y, z; + + float tail[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; + float roll = 0; + int use_connect = -1; + std::string layers; et = etit->second; - et->setData("tip_x", &x); - et->setData("tip_y", &y); - et->setData("tip_z", &z); - float vec[3] = { x, y, z }; - copy_v3_v3(bone->tail, bone->head); - add_v3_v3v3(bone->tail, bone->head, vec); + + bool has_tail = false; + has_tail |= et->setData("tip_x", &tail[0]); + has_tail |= et->setData("tip_y", &tail[1]); + has_tail |= et->setData("tip_z", &tail[2]); + + bool has_connect = et->setData("connect", &use_connect); + bool has_roll = et->setData("roll", &roll); + + layers = et->setData("layer", layers); + + if (has_tail && !has_connect) + { + use_connect = 0; // got a bone tail definition but no connect info -> bone is not connected + } + + be->set_bone_layers(layers, layer_labels); + if (has_tail) be->set_tail(tail); + if (has_roll) be->set_roll(roll); + be->set_use_connect(use_connect); } + be->set_leaf_bone(true); - BoneExtended *be = new BoneExtended(bone); - extended_bones[bone->name] = be; return *be; } diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h index 732fda80ff1..f38bd1a6c66 100644 --- a/source/blender/collada/ArmatureImporter.h +++ b/source/blender/collada/ArmatureImporter.h @@ -59,25 +59,6 @@ extern "C" { #define UNLIMITED_CHAIN_MAX INT_MAX #define MINIMUM_BONE_LENGTH 0.000001f -class BoneExtended { - -private: - char name[MAXBONENAME]; - int chain_length; - bool is_leaf; - -public: - - BoneExtended(EditBone *aBone); - char *get_name(); - int get_chain_length(); - - void set_name(char *aName); - void set_chain_length(const int aLength); - void set_leaf_bone(bool state); - bool is_leaf_bone(); -}; - class ArmatureImporter : private TransformReader { private: @@ -125,12 +106,13 @@ private: #endif int create_bone(SkinInfo* skin, COLLADAFW::Node *node, EditBone *parent, int totchild, - float parent_mat[4][4], bArmature *arm); + float parent_mat[4][4], bArmature *arm, std::vector<std::string> &layer_labels); - BoneExtended &add_bone_extended(EditBone *bone, COLLADAFW::Node * node); + BoneExtended &add_bone_extended(EditBone *bone, COLLADAFW::Node * node, std::vector<std::string> &layer_labels); void clear_extended_boneset(); void fix_leaf_bones(bArmature *armature, Bone *bone); + void fix_parent_connect(bArmature *armature, Bone *bone); void connect_bone_chains(bArmature *armature, Bone *bone, const int max_chain_length); void set_pose( Object *ob_arm, COLLADAFW::Node *root_node, const char *parentname, float parent_mat[4][4]); diff --git a/source/blender/collada/ExportSettings.h b/source/blender/collada/ExportSettings.h index 3dc7e74379e..9451cac9dae 100644 --- a/source/blender/collada/ExportSettings.h +++ b/source/blender/collada/ExportSettings.h @@ -48,6 +48,7 @@ public: bool triangulate; bool use_object_instantiation; + bool use_blender_profile; bool sort_by_name; BC_export_transformation_type export_transformation_type; bool open_sim; diff --git a/source/blender/collada/ExtraTags.cpp b/source/blender/collada/ExtraTags.cpp index 6af61432fda..ea225d8a4ae 100644 --- a/source/blender/collada/ExtraTags.cpp +++ b/source/blender/collada/ExtraTags.cpp @@ -85,32 +85,45 @@ std::string ExtraTags::asString(std::string tag, bool *ok) } -void ExtraTags::setData(std::string tag, short *data) +bool ExtraTags::setData(std::string tag, short *data) { bool ok = false; int tmp = asInt(tag, &ok); if (ok) *data = (short)tmp; + return ok; } -void ExtraTags::setData(std::string tag, int *data) + +bool ExtraTags::setData(std::string tag, int *data) { bool ok = false; int tmp = asInt(tag, &ok); if (ok) *data = tmp; + return ok; } -void ExtraTags::setData(std::string tag, float *data) + +bool ExtraTags::setData(std::string tag, float *data) { bool ok = false; float tmp = asFloat(tag, &ok); if (ok) *data = tmp; + return ok; } -void ExtraTags::setData(std::string tag, char *data) + +bool ExtraTags::setData(std::string tag, char *data) { bool ok = false; int tmp = asInt(tag, &ok); if (ok) *data = (char)tmp; + return ok; +} + +std::string ExtraTags::setData(std::string tag, std::string &data) +{ + bool ok = false; + std::string tmp = asString(tag, &ok); + return (ok) ? tmp : data; } - diff --git a/source/blender/collada/ExtraTags.h b/source/blender/collada/ExtraTags.h index 03a311a7e86..ad272dcba65 100644 --- a/source/blender/collada/ExtraTags.h +++ b/source/blender/collada/ExtraTags.h @@ -43,17 +43,18 @@ public: bool addTag(std::string tag, std::string data); /** Set given short pointer to value of tag, if it exists. */ - void setData(std::string tag, short *data); + bool setData(std::string tag, short *data); /** Set given int pointer to value of tag, if it exists. */ - void setData(std::string tag, int *data); + bool setData(std::string tag, int *data); /** Set given float pointer to value of tag, if it exists. */ - void setData(std::string tag, float *data); + bool setData(std::string tag, float *data); /** Set given char pointer to value of tag, if it exists. */ - void setData(std::string tag, char *data); - + bool setData(std::string tag, char *data); + std::string setData(std::string tag, std::string &data); + /** Return true if the extra tags is for specified profile. */ bool isProfile(std::string profile); diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index a884268fd5e..76f24545248 100644 --- a/source/blender/collada/MeshImporter.cpp +++ b/source/blender/collada/MeshImporter.cpp @@ -259,7 +259,8 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); const std::string &name = bc_get_dae_name(mesh); - + int hole_count = 0; + for (unsigned i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; @@ -275,13 +276,21 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su for (unsigned int j = 0; j < vca.getCount(); j++) { int count = vca[j]; - if (count < 3) { - fprintf(stderr, "Primitive %s in %s has at least one face with vertex count < 3\n", + if (abs(count) < 3) { + fprintf(stderr, "ERROR: Primitive %s in %s has at least one face with vertex count < 3\n", type_str, name.c_str()); return false; } + if (count < 0) + { + hole_count ++; + } + } + + if (hole_count > 0) + { + fprintf(stderr, "WARNING: Primitive %s in %s: %d holes not imported (unsupported)\n", type_str, name.c_str(), hole_count); } - } else if (type == COLLADAFW::MeshPrimitive::LINES) { @@ -289,13 +298,13 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su } else if (type != COLLADAFW::MeshPrimitive::TRIANGLES && type != COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { - fprintf(stderr, "Primitive type %s is not supported.\n", type_str); + fprintf(stderr, "ERROR: Primitive type %s is not supported.\n", type_str); return false; } } if (mesh->getPositions().empty()) { - fprintf(stderr, "Mesh %s has no vertices.\n", name.c_str()); + fprintf(stderr, "ERROR: Mesh %s has no vertices.\n", name.c_str()); return false; } @@ -409,11 +418,18 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me) size_t prim_poly_count = mpvc->getFaceCount(); size_t prim_loop_count = 0; - for (int index=0; index < prim_poly_count; index++) { - prim_loop_count += get_vertex_count(mpvc, index); + for (int index=0; index < prim_poly_count; index++) + { + int vcount = get_vertex_count(mpvc, index); + if (vcount > 0) { + prim_loop_count += vcount; + total_poly_count++; + } + else { + // TODO: this is a hole and not another polygon! + } } - total_poly_count += prim_poly_count; total_loop_count += prim_loop_count; break; @@ -683,9 +699,12 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) COLLADAFW::IndexListArray& index_list_array_vcolor = mp->getColorIndicesArray(); for (unsigned int j = 0; j < prim_totpoly; j++) { - + // Vertices in polygon: int vcount = get_vertex_count(mpvc, j); + if (vcount < 0) { + continue; // TODO: add support for holes + } set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index b64b10e0833..e1b8a2dd30a 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -81,8 +81,9 @@ int collada_export(Scene *sce, int use_texture_copies, int triangulate, - int use_object_instantiation, - int sort_by_name, + int use_object_instantiation, + int use_blender_profile, + int sort_by_name, BC_export_transformation_type export_transformation_type, int open_sim) { @@ -105,6 +106,7 @@ int collada_export(Scene *sce, export_settings.triangulate = triangulate != 0; export_settings.use_object_instantiation = use_object_instantiation != 0; + export_settings.use_blender_profile = use_blender_profile != 0; export_settings.sort_by_name = sort_by_name != 0; export_settings.export_transformation_type = export_transformation_type; export_settings.open_sim = open_sim != 0; diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h index 6819a62fdf0..db8ea884222 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -78,6 +78,7 @@ int collada_export(struct Scene *sce, int triangulate, int use_object_instantiation, + int use_blender_profile, int sort_by_name, BC_export_transformation_type export_transformation_type, int open_sim); diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index f8feed8145c..f0984fbc127 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -54,6 +54,8 @@ extern "C" { #include "BKE_scene.h" #include "BKE_DerivedMesh.h" +#include "ED_armature.h" + #include "WM_api.h" // XXX hrm, see if we can do without this #include "WM_types.h" @@ -366,3 +368,212 @@ void bc_triangulate_mesh(Mesh *me) BM_mesh_bm_to_me(bm, me, &bm_to_me_params); BM_mesh_free(bm); } + +/* +* A bone is a leaf when it has no children or all children are not connected. +*/ +bool bc_is_leaf_bone(Bone *bone) +{ + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + if (child->flag & BONE_CONNECTED) + return false; + } + return true; +} + +EditBone *bc_get_edit_bone(bArmature * armature, char *name) { + EditBone *eBone; + + for (eBone = (EditBone *)armature->edbo->first; eBone; eBone = eBone->next) { + if (STREQ(name, eBone->name)) + return eBone; + } + + return NULL; + +} +int bc_set_layer(int bitfield, int layer) +{ + return bc_set_layer(bitfield, layer, true); /* enable */ +} + +int bc_set_layer(int bitfield, int layer, bool enable) +{ + int bit = 1u << layer; + + if (enable) + bitfield |= bit; + else + bitfield &= ~bit; + + return bitfield; +} + +/** +* BoneExtended is a helper class needed for the Bone chain finder +* See ArmatureImporter::fix_leaf_bones() +* and ArmatureImporter::connect_bone_chains() +**/ + +BoneExtended::BoneExtended(EditBone *aBone) +{ + this->set_name(aBone->name); + this->chain_length = 0; + this->is_leaf = false; + this->tail[0] = 0.0f; + this->tail[1] = 0.5f; + this->tail[2] = 0.0f; + this->use_connect = -1; + this->roll = 0; + this->bone_layers = 0; + + this->has_custom_tail = false; + this->has_custom_roll = false; +} + +char *BoneExtended::get_name() +{ + return name; +} + +void BoneExtended::set_name(char *aName) +{ + BLI_strncpy(name, aName, MAXBONENAME); +} + +int BoneExtended::get_chain_length() +{ + return chain_length; +} + +void BoneExtended::set_chain_length(const int aLength) +{ + chain_length = aLength; +} + +void BoneExtended::set_leaf_bone(bool state) +{ + is_leaf = state; +} + +bool BoneExtended::is_leaf_bone() +{ + return is_leaf; +} + +void BoneExtended::set_roll(float roll) +{ + this->roll = roll; + this->has_custom_roll = true; +} + +bool BoneExtended::has_roll() +{ + return this->has_custom_roll; +} + +float BoneExtended::get_roll() +{ + return this->roll; +} + +void BoneExtended::set_tail(float vec[]) +{ + this->tail[0] = vec[0]; + this->tail[1] = vec[1]; + this->tail[2] = vec[2]; + this->has_custom_tail = true; +} + +bool BoneExtended::has_tail() +{ + return this->has_custom_tail; +} + +float *BoneExtended::get_tail() +{ + return this->tail; +} + +inline bool isInteger(const std::string & s) +{ + if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false; + + char * p; + strtol(s.c_str(), &p, 10); + + return (*p == 0); +} + +void BoneExtended::set_bone_layers(std::string layerString, std::vector<std::string> &layer_labels) +{ + std::stringstream ss(layerString); + std::string layer; + int pos; + + while (ss >> layer) { + + /* Blender uses numbers to specify layers*/ + if (isInteger(layer)) + { + pos = atoi(layer.c_str()); + if (pos >= 0 && pos < 32) { + this->bone_layers = bc_set_layer(this->bone_layers, pos); + continue; + } + } + + /* layer uses labels (not supported by blender). Map to layer numbers:*/ + pos = find(layer_labels.begin(), layer_labels.end(), layer) - layer_labels.begin(); + if (pos >= layer_labels.size()) { + layer_labels.push_back(layer); /* remember layer number for future usage*/ + } + + if (pos > 31) + { + fprintf(stderr, "Too many layers in Import. Layer %s mapped to Blender layer 31\n", layer.c_str()); + pos = 31; + } + + /* If numeric layers and labeled layers are used in parallel (unlikely), + we get a potential mixup. Just leave as is for now. + */ + this->bone_layers = bc_set_layer(this->bone_layers, pos); + + } +} + +std::string BoneExtended::get_bone_layers(int bitfield) +{ + std::string result = ""; + std::string sep = ""; + int bit = 1u; + + std::ostringstream ss; + for (int i = 0; i < 32; i++) + { + if (bit & bitfield) + { + ss << sep << i; + sep = " "; + } + bit = bit << 1; + } + return ss.str(); +} + +int BoneExtended::get_bone_layers() +{ + return (bone_layers == 0) ? 1 : bone_layers; // ensure that the bone is in at least one bone layer! +} + + +void BoneExtended::set_use_connect(int use_connect) +{ + this->use_connect = use_connect; +} + +int BoneExtended::get_use_connect() +{ + return this->use_connect; +} diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h index 4bc2f55cf33..74f8dca1492 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -34,6 +34,7 @@ #include <vector> #include <map> +#include <algorithm> extern "C" { #include "DNA_object_types.h" @@ -46,6 +47,7 @@ extern "C" { #include "BLI_linklist.h" #include "BLI_utildefines.h" +#include "BLI_string.h" #include "BKE_context.h" #include "BKE_object.h" @@ -87,7 +89,10 @@ extern void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_sce extern void bc_match_scale(std::vector<Object *> *objects_done, UnitConverter &unit_converter, bool scale_to_scene); extern void bc_triangulate_mesh(Mesh *me); - +extern bool bc_is_leaf_bone(Bone *bone); +extern EditBone *bc_get_edit_bone(bArmature * armature, char *name); +extern int bc_set_layer(int bitfield, int layer, bool enable); +extern int bc_set_layer(int bitfield, int layer); class BCPolygonNormalsIndices { @@ -105,4 +110,48 @@ class BCPolygonNormalsIndices }; +class BoneExtended { + +private: + char name[MAXBONENAME]; + int chain_length; + bool is_leaf; + float tail[3]; + float roll; + + int bone_layers; + bool use_connect; + bool has_custom_tail; + bool has_custom_roll; + +public: + + BoneExtended(EditBone *aBone); + + void set_name(char *aName); + char *get_name(); + + void set_chain_length(const int aLength); + int get_chain_length(); + + void set_leaf_bone(bool state); + bool is_leaf_bone(); + + void set_bone_layers(std::string layers, std::vector<std::string> &layer_labels); + int get_bone_layers(); + static std::string get_bone_layers(int bitfield); + + void set_roll(float roll); + bool has_roll(); + float get_roll(); + + void set_tail(float *vec); + float *get_tail(); + bool has_tail(); + + void set_use_connect(int use_connect); + int get_use_connect(); +}; + + #endif diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 2b4df85f29c..fd2a521bec5 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -25,8 +25,6 @@ set(INC . - intern - util ../blenkernel ../blenlib ../bmesh @@ -42,45 +40,50 @@ set(INC_SYS ) set(SRC + intern/builder/deg_builder.cc + intern/builder/deg_builder_cycle.cc + intern/builder/deg_builder_nodes.cc + intern/builder/deg_builder_pchanmap.cc + intern/builder/deg_builder_relations.cc + intern/builder/deg_builder_transitive.cc + intern/debug/deg_debug_graphviz.cc + intern/eval/deg_eval.cc + intern/eval/deg_eval_debug.cc + intern/eval/deg_eval_flush.cc + intern/nodes/deg_node.cc + intern/nodes/deg_node_component.cc + intern/nodes/deg_node_operation.cc intern/depsgraph.cc - intern/depsnode.cc - intern/depsnode_component.cc - intern/depsnode_operation.cc intern/depsgraph_build.cc - intern/depsgraph_build_nodes.cc - intern/depsgraph_build_relations.cc intern/depsgraph_debug.cc intern/depsgraph_eval.cc intern/depsgraph_query.cc - intern/depsgraph_queue.cc intern/depsgraph_tag.cc intern/depsgraph_type_defines.cc - util/depsgraph_util_cycle.cc - util/depsgraph_util_pchanmap.cc - util/depsgraph_util_transitive.cc DEG_depsgraph.h DEG_depsgraph_build.h DEG_depsgraph_debug.h DEG_depsgraph_query.h + + intern/builder/deg_builder.h + intern/builder/deg_builder_cycle.h + intern/builder/deg_builder_nodes.h + intern/builder/deg_builder_pchanmap.h + intern/builder/deg_builder_relations.h + intern/builder/deg_builder_transitive.h + intern/eval/deg_eval.h + intern/eval/deg_eval_debug.h + intern/eval/deg_eval_flush.h + intern/nodes/deg_node.h + intern/nodes/deg_node_component.h + intern/nodes/deg_node_operation.h intern/depsgraph.h - intern/depsnode.h - intern/depsnode_component.h - intern/depsnode_operation.h - intern/depsnode_opcodes.h - intern/depsgraph_build.h - intern/depsgraph_debug.h intern/depsgraph_intern.h - intern/depsgraph_queue.h intern/depsgraph_types.h - util/depsgraph_util_cycle.h - util/depsgraph_util_function.h - util/depsgraph_util_hash.h - util/depsgraph_util_map.h - util/depsgraph_util_pchanmap.h - util/depsgraph_util_set.h - util/depsgraph_util_transitive.h + util/deg_util_function.h + util/deg_util_hash.h ) if(WITH_CXX11) diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index f37ba71ab65..d1de83ec8a9 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -131,9 +131,6 @@ void DEG_ids_clear_recalc(struct Main *bmain); /* Update Flushing ------------------------------- */ -/* Flush updates */ -void DEG_graph_flush_updates(struct Main *bmain, Depsgraph *graph); - /* Flush updates for all IDs */ void DEG_ids_flush_tagged(struct Main *bmain); @@ -144,11 +141,6 @@ void DEG_ids_check_recalc(struct Main *bmain, struct Scene *scene, bool time); -/* Clear all update tags - * - For aborted updates, or after successful evaluation - */ -void DEG_graph_clear_tags(Depsgraph *graph); - /* ************************************************ */ /* Evaluation Engine API */ diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index f680c47247a..49b648c7dae 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -43,9 +43,6 @@ struct Depsgraph; struct Main; struct Scene; -struct PointerRNA; -struct PropertyRNA; - #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/depsgraph/DEG_depsgraph_debug.h b/source/blender/depsgraph/DEG_depsgraph_debug.h index 374fad63c34..0d19b8e1e97 100644 --- a/source/blender/depsgraph/DEG_depsgraph_debug.h +++ b/source/blender/depsgraph/DEG_depsgraph_debug.h @@ -39,13 +39,10 @@ extern "C" { #endif -struct DepsgraphSettings; struct GHash; struct ID; struct Depsgraph; -struct DepsNode; -struct DepsRelation; /* ************************************************ */ /* Statistics */ diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index 60d673d4c5b..ccd204a2083 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -19,7 +19,7 @@ * All rights reserved. * * Original Author: Joshua Leung - * Contributor(s): None Yet + * Contributor(s): Sergey Sharybin * * ***** END GPL LICENSE BLOCK ***** */ @@ -33,155 +33,14 @@ #ifndef __DEG_DEPSGRAPH_QUERY_H__ #define __DEG_DEPSGRAPH_QUERY_H__ -struct ListBase; struct ID; struct Depsgraph; -struct DepsNode; -struct DepsRelation; #ifdef __cplusplus extern "C" { #endif -/* ************************************************ */ -/* Type Defines */ - -/* FilterPredicate Callback - * - * Defines a callback function which can be supplied to check whether a - * node is relevant or not. - * - * < graph: Depsgraph that we're traversing - * < node: The node to check - * < userdata: FilterPredicate state data (as needed) - * > returns: True if node is relevant - */ -typedef bool (*DEG_FilterPredicate)(const struct Depsgraph *graph, const struct DepsNode *node, void *userdata); - - -/* Node Operation - * - * Performs some action on the given node, provided that the node was - * deemed to be relevant to operate on. - * - * < graph: Depsgraph that we're traversing - * < node: The node to perform operation on/with - * < userdata: Node Operation's state data (as needed) - * > returns: True if traversal should be aborted at this point - */ -typedef bool (*DEG_NodeOperation)(const struct Depsgraph *graph, struct DepsNode *node, void *userdata); - -/* ************************************************ */ -/* Low-Level Filtering API */ - -/* Create a filtered copy of the given graph which contains only the - * nodes which fulfill the criteria specified using the FilterPredicate - * passed in. - * - * < graph: The graph to be copied and filtered - * < filter: FilterPredicate used to check which nodes should be included - * (If null, full graph is copied as-is) - * < userdata: State data for filter (as necessary) - * - * > returns: a full copy of all the relevant nodes - the matching subgraph - */ -// XXX: is there any need for extra settings/options for how the filtering goes? -Depsgraph *DEG_graph_filter(const struct Depsgraph *graph, DEG_FilterPredicate *filter, void *userdata); - - -/* Traverse nodes in graph which are deemed relevant, - * performing the provided operation on the nodes. - * - * < graph: The graph to perform operations on - * < filter: FilterPredicate used to check which nodes should be included - * (If null, all nodes are considered valid targets) - * < filter_data: Custom state data for FilterPredicate - * (Note: This can be the same as op_data, where appropriate) - * < op: NodeOperation to perform on each node - * (If null, no graph traversal is performed for efficiency) - * < op_data: Custom state data for NodeOperation - * (Note: This can be the same as filter_data, where appropriate) - */ -void DEG_graph_traverse(const struct Depsgraph *graph, - DEG_FilterPredicate *filter, void *filter_data, - DEG_NodeOperation *op, void *op_data); - -/* ************************************************ */ -/* Node-Based Operations */ -// XXX: do we want to be able to attach conditional requirements here? - -/* Find an (outer) node matching given conditions - * ! Assumes that there will only be one such node, or that only the first one matters - * - * < graph: a dependency graph which may or may not contain a node matching these requirements - * < query: query conditions for the criteria that the node must satisfy - */ -//DepsNode *DEG_node_find(const Depsgraph *graph, DEG_QueryConditions *query); - -/* Topology Queries (Direct) ---------------------- */ - -/* Get list of nodes which directly depend on given node - * - * > result: list to write results to - * < node: the node to find the children/dependents of - */ -void DEG_node_get_children(struct ListBase *result, const struct DepsNode *node); - - -/* Get list of nodes which given node directly depends on - * - * > result: list to write results to - * < node: the node to find the dependencies of - */ -void DEG_node_get_dependencies(struct ListBase *result, const struct DepsNode *node); - - -/* Topology Queries (Subgraph) -------------------- */ -// XXX: given that subgraphs potentially involve many interconnected nodes, we currently -// just spit out a copy of the subgraph which matches. This works well for the cases -// where these are used - mostly for efficient updating of subsets of the nodes. - -// XXX: allow supplying a filter predicate to provide further filtering/pruning? - - -/* Get all descendants of a node - * - * That is, get the subgraph / subset of nodes which are dependent - * on the results of the given node. - */ -Depsgraph *DEG_node_get_descendants(const struct Depsgraph *graph, const struct DepsNode *node); - - -/* Get all ancestors of a node - * - * That is, get the subgraph / subset of nodes which the given node - * is dependent on in order to be evaluated. - */ -Depsgraph *DEG_node_get_ancestors(const struct Depsgraph *graph, const struct DepsNode *node); - -/* ************************************************ */ -/* Higher-Level Queries */ - -/* Get ID-blocks which would be affected if specified ID is modified - * < only_direct: True = Only ID-blocks with direct relationships to ID-block will be returned - * - * > result: (LinkData : ID) a list of ID-blocks matching the specified criteria - * > returns: number of matching ID-blocks - */ -size_t DEG_query_affected_ids(struct ListBase *result, const struct ID *id, const bool only_direct); - - -/* Get ID-blocks which are needed to update/evaluate specified ID - * < only_direct: True = Only ID-blocks with direct relationships to ID-block will be returned - * - * > result: (LinkData : ID) a list of ID-blocks matching the specified criteria - * > returns: number of matching ID-blocks - */ -size_t DEG_query_required_ids(struct ListBase *result, const struct ID *id, const bool only_direct); - -/* ************************************************ */ - /* Check if given ID type was tagged for update. */ bool DEG_id_type_tagged(struct Main *bmain, short idtype); diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc new file mode 100644 index 00000000000..9f80c21a6a4 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -0,0 +1,129 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Original Author: Sergey Sharybin + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/build/deg_builder.cc + * \ingroup depsgraph + */ + +#include "intern/builder/deg_builder.h" + +// TODO(sergey): Use own wrapper over STD. +#include <stack> + +#include "DNA_anim_types.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +#include "intern/depsgraph.h" +#include "intern/depsgraph_types.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "util/deg_util_foreach.h" + +namespace DEG { + +string deg_fcurve_id_name(const FCurve *fcu) +{ + char index_buf[32]; + // TODO(sergey): Use int-to-string utility or so. + BLI_snprintf(index_buf, sizeof(index_buf), "[%d]", fcu->array_index); + return string(fcu->rna_path) + index_buf; +} + +void deg_graph_build_finalize(Depsgraph *graph) +{ + std::stack<OperationDepsNode *> stack; + + foreach (OperationDepsNode *node, graph->operations) { + IDDepsNode *id_node = node->owner->owner; + node->done = 0; + node->num_links_pending = 0; + foreach (DepsRelation *rel, node->outlinks) { + if ((rel->from->type == DEPSNODE_TYPE_OPERATION) && + (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) + { + ++node->num_links_pending; + } + } + if (node->num_links_pending == 0) { + stack.push(node); + node->done = 1; + } + node->owner->layers = id_node->layers; + id_node->id->tag |= LIB_TAG_DOIT; + } + + while (!stack.empty()) { + OperationDepsNode *node = stack.top(); + stack.pop(); + /* Flush layers to parents. */ + foreach (DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + from->owner->layers |= node->owner->layers; + } + } + /* Schedule parent nodes. */ + foreach (DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { + BLI_assert(from->num_links_pending > 0); + --from->num_links_pending; + } + if (from->num_links_pending == 0 && from->done == 0) { + stack.push(from); + from->done = 1; + } + } + } + } + + /* Re-tag IDs for update if it was tagged before the relations update tag. */ + GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) + { + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp, id_node->components) + { + id_node->layers |= comp->layers; + } + GHASH_FOREACH_END(); + + ID *id = id_node->id; + if (id->tag & LIB_TAG_ID_RECALC_ALL && + id->tag & LIB_TAG_DOIT) + { + id_node->tag_update(graph); + id->tag &= ~LIB_TAG_DOIT; + } + id_node->finalize_build(); + } + GHASH_FOREACH_END(); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h new file mode 100644 index 00000000000..7ecb4b20684 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -0,0 +1,46 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Original Author: Sergey Sharybin + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/build/deg_builder.h + * \ingroup depsgraph + */ + +#pragma once + +#include "intern/depsgraph_types.h" + +struct FCurve; + +namespace DEG { + +struct Depsgraph; + +/* Get unique identifier for FCurves and Drivers */ +string deg_fcurve_id_name(const FCurve *fcu); + +void deg_graph_build_finalize(struct Depsgraph *graph); + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc index 5eae8c087ad..225cc64ae4d 100644 --- a/source/blender/depsgraph/util/depsgraph_util_cycle.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc @@ -23,28 +23,30 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_cycle.cc +/** \file blender/depsgraph/intern/builder/deg_builder_cycle.cc * \ingroup depsgraph */ +#include "intern/builder/deg_builder_cycle.h" + +// TOO(sergey): Use some wrappers over those? #include <cstdio> #include <cstdlib> #include <stack> extern "C" { #include "BLI_utildefines.h" +} -#include "DNA_ID.h" +#include "util/deg_util_foreach.h" -#include "RNA_access.h" -#include "RNA_types.h" -} +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" -#include "depsgraph_util_cycle.h" -#include "depsgraph.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" +#include "intern/depsgraph.h" + +namespace DEG { struct StackEntry { OperationDepsNode *node; @@ -62,17 +64,9 @@ void deg_graph_detect_cycles(Depsgraph *graph) const int NODE_IN_STACK = 2; std::stack<StackEntry> traversal_stack; - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (OperationDepsNode *node, graph->operations) { bool has_inlinks = false; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; + foreach (DepsRelation *rel, node->inlinks) { if (rel->from->type == DEPSNODE_TYPE_OPERATION) { has_inlinks = true; } @@ -94,11 +88,7 @@ void deg_graph_detect_cycles(Depsgraph *graph) StackEntry &entry = traversal_stack.top(); OperationDepsNode *node = entry.node; bool all_child_traversed = true; - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; + foreach (DepsRelation *rel, node->outlinks) { if (rel->to->type == DEPSNODE_TYPE_OPERATION) { OperationDepsNode *to = (OperationDepsNode *)rel->to; if (to->done == NODE_IN_STACK) { @@ -138,3 +128,5 @@ void deg_graph_detect_cycles(Depsgraph *graph) } } } + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_cycle.h b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h index fac38b61057..386fbd80d19 100644 --- a/source/blender/depsgraph/util/depsgraph_util_cycle.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h @@ -23,15 +23,18 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_cycle.h +/** \file blender/depsgraph/intern/builder/deg_builder_cycle.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_CYCLE_H__ -#define __DEPSGRAPH_UTIL_CYCLE_H__ + +#pragma once + +namespace DEG { struct Depsgraph; +/* Detect and solve dependency cycles. */ void deg_graph_detect_cycles(Depsgraph *graph); -#endif /* __DEPSGRAPH_UTIL_CYCLE_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index be706ffb338..5359cc8754a 100644 --- a/source/blender/depsgraph/intern/depsgraph_build_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -24,12 +24,14 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_build_nodes.cc +/** \file blender/depsgraph/intern/builder/deg_build_nodes.cc * \ingroup depsgraph * * Methods for constructing depsgraph's nodes */ +#include "intern/builder/deg_builder_nodes.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -97,12 +99,14 @@ extern "C" { #include "RNA_types.h" } /* extern "C" */ -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_types.h" -#include "depsgraph_build.h" -#include "depsgraph_intern.h" +#include "intern/builder/deg_builder.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_types.h" +#include "intern/depsgraph_intern.h" + +namespace DEG { /* ************ */ /* Node Builder */ @@ -217,6 +221,17 @@ OperationDepsNode *DepsgraphNodeBuilder::add_operation_node( return add_operation_node(comp_node, optype, op, opcode, description); } +OperationDepsNode *DepsgraphNodeBuilder::add_operation_node( + ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description) +{ + return add_operation_node(id, comp_type, "", optype, op, opcode, description); +} + bool DepsgraphNodeBuilder::has_operation_node(ID *id, eDepsNode_Type comp_type, const string &comp_name, @@ -237,6 +252,14 @@ OperationDepsNode *DepsgraphNodeBuilder::find_operation_node( return comp_node->has_operation(opcode, description); } +OperationDepsNode *DepsgraphNodeBuilder::find_operation_node( + ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Code opcode, + const string& description) +{ + return find_operation_node(id, comp_type, "", opcode, description); +} /* **** Build functions for entity nodes **** */ @@ -343,7 +366,7 @@ SubgraphDepsNode *DepsgraphNodeBuilder::build_subgraph(Group *group) return NULL; /* create new subgraph's data */ - Depsgraph *subgraph = DEG_graph_new(); + Depsgraph *subgraph = reinterpret_cast<Depsgraph *>(DEG_graph_new()); DepsgraphNodeBuilder subgraph_builder(m_bmain, subgraph); @@ -381,11 +404,11 @@ void DepsgraphNodeBuilder::build_object(Scene *scene, Base *base, Object *ob) IDDepsNode *id_node = add_id_node(&ob->id); id_node->layers = base->lay; + ob->customdata_mask = 0; /* standard components */ build_object_transform(scene, ob); - /* object data */ if (ob->data) { /* type-specific data... */ @@ -1234,3 +1257,5 @@ void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd) */ build_animdata(gpd_id); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h new file mode 100644 index 00000000000..6ee0b8406a1 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -0,0 +1,153 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Lukas Toenne + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/builder/deg_builder_nodes.h + * \ingroup depsgraph + */ + +#pragma once + +#include "intern/depsgraph_types.h" + +struct Base; +struct bGPdata; +struct ListBase; +struct GHash; +struct ID; +struct FCurve; +struct Group; +struct Key; +struct Main; +struct Material; +struct MTex; +struct bNodeTree; +struct Object; +struct bPoseChannel; +struct bConstraint; +struct Scene; +struct Tex; +struct World; + +struct PropertyRNA; + +namespace DEG { + +struct Depsgraph; +struct DepsNode; +struct RootDepsNode; +struct SubgraphDepsNode; +struct IDDepsNode; +struct TimeSourceDepsNode; +struct ComponentDepsNode; +struct OperationDepsNode; + +struct DepsgraphNodeBuilder { + DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph); + ~DepsgraphNodeBuilder(); + + RootDepsNode *add_root_node(); + IDDepsNode *add_id_node(ID *id); + TimeSourceDepsNode *add_time_source(ID *id); + + ComponentDepsNode *add_component_node(ID *id, + eDepsNode_Type comp_type, + const string& comp_name = ""); + + OperationDepsNode *add_operation_node(ComponentDepsNode *comp_node, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description = ""); + OperationDepsNode *add_operation_node(ID *id, + eDepsNode_Type comp_type, + const string& comp_name, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description = ""); + OperationDepsNode *add_operation_node(ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description = ""); + + bool has_operation_node(ID *id, + eDepsNode_Type comp_type, + const string& comp_name, + eDepsOperation_Code opcode, + const string& description = ""); + + OperationDepsNode *find_operation_node(ID *id, + eDepsNode_Type comp_type, + const string &comp_name, + eDepsOperation_Code opcode, + const string &description = ""); + + OperationDepsNode *find_operation_node(ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Code opcode, + const string &description = ""); + + void build_scene(Main *bmain, Scene *scene); + SubgraphDepsNode *build_subgraph(Group *group); + void build_group(Scene *scene, Base *base, Group *group); + void build_object(Scene *scene, Base *base, Object *ob); + void build_object_transform(Scene *scene, Object *ob); + void build_object_constraints(Scene *scene, Object *ob); + void build_pose_constraints(Object *ob, bPoseChannel *pchan); + void build_rigidbody(Scene *scene); + void build_particles(Scene *scene, Object *ob); + void build_animdata(ID *id); + OperationDepsNode *build_driver(ID *id, FCurve *fcurve); + void build_ik_pose(Scene *scene, + Object *ob, + bPoseChannel *pchan, + bConstraint *con); + void build_splineik_pose(Scene *scene, + Object *ob, + bPoseChannel *pchan, + bConstraint *con); + void build_rig(Scene *scene, Object *ob); + void build_proxy_rig(Object *ob); + void build_shapekeys(Key *key); + void build_obdata_geom(Scene *scene, Object *ob); + void build_camera(Object *ob); + void build_lamp(Object *ob); + void build_nodetree(DepsNode *owner_node, bNodeTree *ntree); + void build_material(DepsNode *owner_node, Material *ma); + void build_texture(DepsNode *owner_node, Tex *tex); + void build_texture_stack(DepsNode *owner_node, MTex **texture_stack); + void build_world(World *world); + void build_compositor(Scene *scene); + void build_gpencil(bGPdata *gpd); + +protected: + Main *m_bmain; + Depsgraph *m_graph; +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_pchanmap.cc b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc index 80b37ec622d..0e78df52ff8 100644 --- a/source/blender/depsgraph/util/depsgraph_util_pchanmap.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc @@ -24,11 +24,11 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_pchanmap.cc +/** \file blender/depsgraph/intern/builder/deg_builder_pchanmap.h * \ingroup depsgraph */ -#include "depsgraph_util_pchanmap.h" +#include "intern/builder/deg_builder_pchanmap.h" #include <stdio.h> #include <string.h> @@ -38,6 +38,8 @@ extern "C" { #include "BLI_ghash.h" } +namespace DEG { + static void free_rootpchanmap_valueset(void *val) { /* Just need to free the set itself - the names stored are all references. */ @@ -48,13 +50,13 @@ static void free_rootpchanmap_valueset(void *val) RootPChanMap::RootPChanMap() { /* Just create empty map. */ - m_map = BLI_ghash_str_new("RootPChanMap"); + map_ = BLI_ghash_str_new("RootPChanMap"); } RootPChanMap::~RootPChanMap() { /* Free the map, and all the value sets. */ - BLI_ghash_free(m_map, NULL, free_rootpchanmap_valueset); + BLI_ghash_free(map_, NULL, free_rootpchanmap_valueset); } /* Debug contents of map */ @@ -64,7 +66,7 @@ void RootPChanMap::print_debug() GSetIterator it2; printf("Root PChan Map:\n"); - GHASH_ITER(it1, m_map) { + GHASH_ITER(it1, map_) { const char *item = (const char *)BLI_ghashIterator_getKey(&it1); GSet *values = (GSet *)BLI_ghashIterator_getValue(&it1); @@ -80,11 +82,11 @@ void RootPChanMap::print_debug() /* Add a mapping. */ void RootPChanMap::add_bone(const char *bone, const char *root) { - if (BLI_ghash_haskey(m_map, bone)) { + if (BLI_ghash_haskey(map_, bone)) { /* Add new entry, but only add the root if it doesn't already * exist in there. */ - GSet *values = (GSet *)BLI_ghash_lookup(m_map, bone); + GSet *values = (GSet *)BLI_ghash_lookup(map_, bone); BLI_gset_add(values, (void *)root); } else { @@ -92,7 +94,7 @@ void RootPChanMap::add_bone(const char *bone, const char *root) GSet *values = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "RootPChanMap Value Set"); - BLI_ghash_insert(m_map, (void *)bone, (void *)values); + BLI_ghash_insert(map_, (void *)bone, (void *)values); /* Add new entry now. */ BLI_gset_insert(values, (void *)root); @@ -103,20 +105,20 @@ void RootPChanMap::add_bone(const char *bone, const char *root) bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) { /* Ensure that both are in the map... */ - if (BLI_ghash_haskey(m_map, bone1) == false) { + if (BLI_ghash_haskey(map_, bone1) == false) { //fprintf("RootPChanMap: bone1 '%s' not found (%s => %s)\n", bone1, bone1, bone2); //print_debug(); return false; } - if (BLI_ghash_haskey(m_map, bone2) == false) { + if (BLI_ghash_haskey(map_, bone2) == false) { //fprintf("RootPChanMap: bone2 '%s' not found (%s => %s)\n", bone2, bone1, bone2); //print_debug(); return false; } - GSet *bone1_roots = (GSet *)BLI_ghash_lookup(m_map, (void *)bone1); - GSet *bone2_roots = (GSet *)BLI_ghash_lookup(m_map, (void *)bone2); + GSet *bone1_roots = (GSet *)BLI_ghash_lookup(map_, (void *)bone1); + GSet *bone2_roots = (GSet *)BLI_ghash_lookup(map_, (void *)bone2); GSetIterator it1, it2; GSET_ITER(it1, bone1_roots) { @@ -134,3 +136,5 @@ bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) //fprintf("RootPChanMap: No common root found (%s => %s)\n", bone1, bone2); return false; } + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h index b7f4c495933..233d8602fce 100644 --- a/source/blender/depsgraph/util/depsgraph_util_pchanmap.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h @@ -24,12 +24,15 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_pchanmap.h +/** \file blender/depsgraph/intern/builder/deg_builder_pchanmap.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_PCHANMAP_H__ -#define __DEPSGRAPH_UTIL_PCHANMAP_H__ +#pragma once + +struct GHash; + +namespace DEG { struct RootPChanMap { /* ctor and dtor - Create and free the internal map respectively. */ @@ -45,7 +48,7 @@ struct RootPChanMap { /* Check if there's a common root bone between two bones. */ bool has_common_root(const char *bone1, const char *bone2); -private: +protected: /* The actual map: * - Keys are "strings" (const char *) - not dynamically allocated. * - Values are "sets" (const char *) - not dynamically allocated. @@ -53,7 +56,7 @@ private: * We don't use the C++ maps here, as it's more convenient to use * Blender's GHash and be able to compare by-value instead of by-ref. */ - struct GHash *m_map; + struct GHash *map_; }; -#endif /* __DEPSGRAPH_UTIL_PCHANMAP_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 52af483d87c..10aebb75fbf 100644 --- a/source/blender/depsgraph/intern/depsgraph_build_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -24,12 +24,14 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_build_relations.cc +/** \file blender/depsgraph/intern/builder/deg_builder_relations.cc * \ingroup depsgraph * * Methods for constructing depsgraph */ +#include "intern/builder/deg_builder_relations.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -93,15 +95,19 @@ extern "C" { #include "RNA_types.h" } /* extern "C" */ -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_build.h" -#include "depsgraph_debug.h" -#include "depsgraph_intern.h" -#include "depsgraph_types.h" +#include "intern/builder/deg_builder.h" +#include "intern/builder/deg_builder_pchanmap.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "intern/depsgraph_types.h" + +#include "util/deg_util_foreach.h" -#include "depsgraph_util_pchanmap.h" +namespace DEG { /* ***************** */ /* Relations Builder */ @@ -304,6 +310,19 @@ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) if (scene->gpd) { build_gpencil(&scene->id, scene->gpd); } + + for (Depsgraph::OperationNodes::const_iterator it_op = m_graph->operations.begin(); + it_op != m_graph->operations.end(); + ++it_op) + { + OperationDepsNode *node = *it_op; + IDDepsNode *id_node = node->owner->owner; + ID *id = id_node->id; + if (GS(id->name) == ID_OB) { + Object *object = (Object *)id; + object->customdata_mask |= node->customdata_mask; + } + } } void DepsgraphRelationBuilder::build_group(Main *bmain, @@ -473,8 +492,12 @@ void DepsgraphRelationBuilder::build_object_parent(Object *ob) { ComponentKey parent_key(&ob->parent->id, DEPSNODE_TYPE_GEOMETRY); add_relation(parent_key, ob_key, DEPSREL_TYPE_GEOMETRY_EVAL, "Vertex Parent"); + /* XXX not sure what this is for or how you could be done properly - lukas */ - //parent_node->customdata_mask |= CD_MASK_ORIGINDEX; + OperationDepsNode *parent_node = find_operation_node(parent_key); + if (parent_node != NULL) { + parent_node->customdata_mask |= CD_MASK_ORIGINDEX; + } ComponentKey transform_key(&ob->parent->id, DEPSNODE_TYPE_TRANSFORM); add_relation(transform_key, ob_key, DEPSREL_TYPE_TRANSFORM, "Vertex Parent TFM"); @@ -625,7 +648,10 @@ void DepsgraphRelationBuilder::build_constraints(Scene *scene, ID *id, eDepsNode add_relation(target_key, constraint_op_key, DEPSREL_TYPE_GEOMETRY_EVAL, cti->name); if (ct->tar->type == OB_MESH) { - //node2->customdata_mask |= CD_MASK_MDEFORMVERT; + OperationDepsNode *node2 = find_operation_node(target_key); + if (node2 != NULL) { + node2->customdata_mask |= CD_MASK_MDEFORMVERT; + } } } else if (con->type == CONSTRAINT_TYPE_SHRINKWRAP) { @@ -766,8 +792,7 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) if (arm_node && bone_name) { /* find objects which use this, and make their eval callbacks depend on this */ - DEPSNODE_RELATIONS_ITER_BEGIN(arm_node->outlinks, rel) - { + foreach (DepsRelation *rel, arm_node->outlinks) { IDDepsNode *to_node = (IDDepsNode *)rel->to; /* we only care about objects with pose data which use this... */ @@ -781,7 +806,6 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) } } } - DEPSNODE_RELATIONS_ITER_END; /* free temp data */ MEM_freeN(bone_name); @@ -1202,7 +1226,10 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *ob, add_relation(target_key, solver_key, DEPSREL_TYPE_GEOMETRY_EVAL, con->name); if (data->tar->type == OB_MESH) { - //node2->customdata_mask |= CD_MASK_MDEFORMVERT; + OperationDepsNode *node2 = find_operation_node(target_key); + if (node2 != NULL) { + node2->customdata_mask |= CD_MASK_MDEFORMVERT; + } } } else { @@ -1234,7 +1261,10 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *ob, add_relation(target_key, solver_key, DEPSREL_TYPE_GEOMETRY_EVAL, con->name); if (data->poletar->type == OB_MESH) { - //node2->customdata_mask |= CD_MASK_MDEFORMVERT; + OperationDepsNode *node2 = find_operation_node(target_key); + if (node2 != NULL) { + node2->customdata_mask |= CD_MASK_MDEFORMVERT; + } } } else { @@ -1610,13 +1640,18 @@ void DepsgraphRelationBuilder::build_obdata_geom(Main *bmain, Scene *scene, Obje if (mti->updateDepsgraph) { DepsNodeHandle handle = create_node_handle(mod_key); - mti->updateDepsgraph(md, bmain, scene, ob, &handle); + mti->updateDepsgraph( + md, + bmain, + scene, + ob, + reinterpret_cast< ::DepsNodeHandle* >(&handle)); } if (BKE_object_modifier_use_time(ob, md)) { TimeSourceKey time_src_key; add_relation(time_src_key, mod_key, DEPSREL_TYPE_TIME, "Time Source"); - + /* Hacky fix for T45633 (Animated modifiers aren't updated) * * This check works because BKE_object_modifier_use_time() tests @@ -1926,3 +1961,4 @@ bool DepsgraphRelationBuilder::needs_animdata_node(ID *id) return false; } +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index c5b04ec299c..c0bf82becda 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -24,12 +24,27 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_build.h +/** \file blender/depsgraph/intern/builder/deg_builder_relations.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_BUILD_H__ -#define __DEPSGRAPH_BUILD_H__ +#pragma once + +#include <cstdio> + +#include "intern/depsgraph_types.h" + +#include "DNA_ID.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#include "BLI_utildefines.h" +#include "BLI_string.h" + +#include "intern/depsgraph_types.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_operation.h" struct Base; struct bGPdata; @@ -52,6 +67,8 @@ struct World; struct PropertyRNA; +namespace DEG { + struct Depsgraph; struct DepsNode; struct DepsNodeHandle; @@ -63,75 +80,6 @@ struct ComponentDepsNode; struct OperationDepsNode; struct RootPChanMap; -struct DepsgraphNodeBuilder { - DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph); - ~DepsgraphNodeBuilder(); - - RootDepsNode *add_root_node(); - IDDepsNode *add_id_node(ID *id); - TimeSourceDepsNode *add_time_source(ID *id); - - ComponentDepsNode *add_component_node(ID *id, eDepsNode_Type comp_type, const string &comp_name = ""); - - OperationDepsNode *add_operation_node(ComponentDepsNode *comp_node, eDepsOperation_Type optype, - DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &description = ""); - OperationDepsNode *add_operation_node(ID *id, eDepsNode_Type comp_type, const string &comp_name, eDepsOperation_Type optype, - DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &description = ""); - OperationDepsNode *add_operation_node(ID *id, eDepsNode_Type comp_type, eDepsOperation_Type optype, - DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &description = "") - { - return add_operation_node(id, comp_type, "", optype, op, opcode, description); - } - - bool has_operation_node(ID *id, eDepsNode_Type comp_type, const string &comp_name, - eDepsOperation_Code opcode, const string &description = ""); - - OperationDepsNode *find_operation_node(ID *id, - eDepsNode_Type comp_type, - const string &comp_name, - eDepsOperation_Code opcode, - const string &description = ""); - - OperationDepsNode *find_operation_node(ID *id, - eDepsNode_Type comp_type, - eDepsOperation_Code opcode, - const string &description = "") - { - return find_operation_node(id, comp_type, "", opcode, description); - } - - void build_scene(Main *bmain, Scene *scene); - SubgraphDepsNode *build_subgraph(Group *group); - void build_group(Scene *scene, Base *base, Group *group); - void build_object(Scene *scene, Base *base, Object *ob); - void build_object_transform(Scene *scene, Object *ob); - void build_object_constraints(Scene *scene, Object *ob); - void build_pose_constraints(Object *ob, bPoseChannel *pchan); - void build_rigidbody(Scene *scene); - void build_particles(Scene *scene, Object *ob); - void build_animdata(ID *id); - OperationDepsNode *build_driver(ID *id, FCurve *fcurve); - void build_ik_pose(Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con); - void build_splineik_pose(Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con); - void build_rig(Scene *scene, Object *ob); - void build_proxy_rig(Object *ob); - void build_shapekeys(Key *key); - void build_obdata_geom(Scene *scene, Object *ob); - void build_camera(Object *ob); - void build_lamp(Object *ob); - void build_nodetree(DepsNode *owner_node, bNodeTree *ntree); - void build_material(DepsNode *owner_node, Material *ma); - void build_texture(DepsNode *owner_node, Tex *tex); - void build_texture_stack(DepsNode *owner_node, MTex **texture_stack); - void build_world(World *world); - void build_compositor(Scene *scene); - void build_gpencil(bGPdata *gpd); - -private: - Main *m_bmain; - Depsgraph *m_graph; -}; - struct RootKey { RootKey() {} @@ -164,7 +112,7 @@ struct ComponentKey const char *idname = (id) ? id->name : "<None>"; char typebuf[5]; - sprintf(typebuf, "%d", type); + BLI_snprintf(typebuf, sizeof(typebuf), "%d", type); return string("ComponentKey(") + idname + ", " + typebuf + ", '" + name + "')"; } @@ -204,7 +152,7 @@ struct OperationKey string identifier() const { char typebuf[5]; - sprintf(typebuf, "%d", component_type); + BLI_snprintf(typebuf, sizeof(typebuf), "%d", component_type); return string("OperationKey(") + "t: " + typebuf + ", cn: '" + component_name + "', c: " + DEG_OPNAMES[opcode] + ", n: '" + name + "')"; } @@ -245,30 +193,45 @@ struct DepsgraphRelationBuilder DepsgraphRelationBuilder(Depsgraph *graph); template <typename KeyFrom, typename KeyTo> - void add_relation(const KeyFrom &key_from, const KeyTo &key_to, - eDepsRelation_Type type, const char *description); + void add_relation(const KeyFrom& key_from, + const KeyTo& key_to, + eDepsRelation_Type type, + const char *description); template <typename KeyTo> - void add_relation(const TimeSourceKey &key_from, const KeyTo &key_to, - eDepsRelation_Type type, const char *description); + void add_relation(const TimeSourceKey& key_from, + const KeyTo& key_to, + eDepsRelation_Type type, + const char *description); template <typename KeyType> - void add_node_handle_relation(const KeyType &key_from, const DepsNodeHandle *handle, - eDepsRelation_Type type, const char *description); + void add_node_handle_relation(const KeyType& key_from, + const DepsNodeHandle *handle, + eDepsRelation_Type type, + const char *description); void build_scene(Main *bmain, Scene *scene); void build_group(Main *bmain, Scene *scene, Object *object, Group *group); void build_object(Main *bmain, Scene *scene, Object *ob); void build_object_parent(Object *ob); - void build_constraints(Scene *scene, ID *id, eDepsNode_Type component_type, const char *component_subdata, - ListBase *constraints, RootPChanMap *root_map); + void build_constraints(Scene *scene, ID *id, + eDepsNode_Type component_type, + const char *component_subdata, + ListBase *constraints, + RootPChanMap *root_map); void build_animdata(ID *id); void build_driver(ID *id, FCurve *fcurve); void build_world(World *world); void build_rigidbody(Scene *scene); void build_particles(Scene *scene, Object *ob); - void build_ik_pose(Object *ob, bPoseChannel *pchan, bConstraint *con, RootPChanMap *root_map); - void build_splineik_pose(Object *ob, bPoseChannel *pchan, bConstraint *con, RootPChanMap *root_map); + void build_ik_pose(Object *ob, + bPoseChannel *pchan, + bConstraint *con, + RootPChanMap *root_map); + void build_splineik_pose(Object *ob, + bPoseChannel *pchan, + bConstraint *con, + RootPChanMap *root_map); void build_rig(Scene *scene, Object *ob); void build_proxy_rig(Object *ob); void build_shapekeys(ID *obdata, Key *key); @@ -282,6 +245,9 @@ struct DepsgraphRelationBuilder void build_compositor(Scene *scene); void build_gpencil(ID *owner, bGPdata *gpd); + template <typename KeyType> + OperationDepsNode *find_operation_node(const KeyType &key); + protected: RootDepsNode *find_node(const RootKey &key) const; TimeSourceDepsNode *find_node(const TimeSourceKey &key) const; @@ -290,12 +256,17 @@ protected: DepsNode *find_node(const RNAPathKey &key) const; OperationDepsNode *has_node(const OperationKey &key) const; - void add_time_relation(TimeSourceDepsNode *timesrc, DepsNode *node_to, const char *description); - void add_operation_relation(OperationDepsNode *node_from, OperationDepsNode *node_to, - eDepsRelation_Type type, const char *description); + void add_time_relation(TimeSourceDepsNode *timesrc, + DepsNode *node_to, + const char *description); + void add_operation_relation(OperationDepsNode *node_from, + OperationDepsNode *node_to, + eDepsRelation_Type type, + const char *description); template <typename KeyType> - DepsNodeHandle create_node_handle(const KeyType &key, const string &default_name = ""); + DepsNodeHandle create_node_handle(const KeyType& key, + const string& default_name = ""); bool needs_animdata_node(ID *id); @@ -320,8 +291,11 @@ struct DepsNodeHandle /* Utilities for Builders ----------------------------------------------------- */ -/* Get unique identifier for FCurves and Drivers */ -string deg_fcurve_id_name(const FCurve *fcu); +template <typename KeyType> +OperationDepsNode *DepsgraphRelationBuilder::find_operation_node(const KeyType& key) { + DepsNode *node = find_node(key); + return node != NULL ? node->get_exit_operation() : NULL; +} template <typename KeyFrom, typename KeyTo> void DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from, @@ -377,10 +351,11 @@ void DepsgraphRelationBuilder::add_relation(const TimeSourceKey &key_from, } template <typename KeyType> -void DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_from, - const DepsNodeHandle *handle, - eDepsRelation_Type type, - const char *description) +void DepsgraphRelationBuilder::add_node_handle_relation( + const KeyType &key_from, + const DepsNodeHandle *handle, + eDepsRelation_Type type, + const char *description) { DepsNode *node_from = find_node(key_from); OperationDepsNode *op_from = node_from ? node_from->get_exit_operation() : NULL; @@ -399,10 +374,11 @@ void DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_from, } template <typename KeyType> -DepsNodeHandle DepsgraphRelationBuilder::create_node_handle(const KeyType &key, - const string &default_name) +DepsNodeHandle DepsgraphRelationBuilder::create_node_handle( + const KeyType &key, + const string &default_name) { return DepsNodeHandle(this, find_node(key), default_name); } -#endif /* __DEPSGRAPH_BUILD_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_transitive.cc b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc index 98192a9540f..0322ef7fa1d 100644 --- a/source/blender/depsgraph/util/depsgraph_util_transitive.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc @@ -24,26 +24,25 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_transitive.cc +/** \file blender/depsgraph/intern/builder/deg_builder_transitive.cc * \ingroup depsgraph */ +#include "intern/builder/deg_builder_transitive.h" + extern "C" { #include "MEM_guardedalloc.h" +} -#include "BLI_utildefines.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" -#include "DNA_ID.h" +#include "intern/depsgraph.h" -#include "RNA_access.h" -#include "RNA_types.h" -} +#include "util/deg_util_foreach.h" -#include "depsgraph_util_transitive.h" -#include "depsgraph.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" +namespace DEG { /* -------------------------------------------------- */ @@ -67,16 +66,11 @@ enum { static void deg_graph_tag_paths_recursive(DepsNode *node) { - if (node->done & OP_VISITED) + if (node->done & OP_VISITED) { return; + } node->done |= OP_VISITED; - - for (OperationDepsNode::Relations::const_iterator it = node->inlinks.begin(); - it != node->inlinks.end(); - ++it) - { - DepsRelation *rel = *it; - + foreach (DepsRelation *rel, node->inlinks) { deg_graph_tag_paths_recursive(rel->from); /* Do this only in inlinks loop, so the target node does not get * flagged. @@ -87,18 +81,9 @@ static void deg_graph_tag_paths_recursive(DepsNode *node) void deg_graph_transitive_reduction(Depsgraph *graph) { - for (Depsgraph::OperationNodes::const_iterator it_target = graph->operations.begin(); - it_target != graph->operations.end(); - ++it_target) - { - OperationDepsNode *target = *it_target; - + foreach (OperationDepsNode *target, graph->operations) { /* Clear tags. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; + foreach (OperationDepsNode *node, graph->operations) { node->done = 0; } @@ -107,19 +92,14 @@ void deg_graph_transitive_reduction(Depsgraph *graph) * flagged. */ target->done |= OP_VISITED; - for (OperationDepsNode::Relations::const_iterator it = target->inlinks.begin(); - it != target->inlinks.end(); - ++it) - { - DepsRelation *rel = *it; - + foreach (DepsRelation *rel, target->inlinks) { deg_graph_tag_paths_recursive(rel->from); } - /* Eemove redundant paths to the target. */ + /* Remove redundant paths to the target. */ for (DepsNode::Relations::const_iterator it_rel = target->inlinks.begin(); it_rel != target->inlinks.end(); - ) + ) { DepsRelation *rel = *it_rel; /* Increment in advance, so we can safely remove the relation. */ @@ -137,3 +117,5 @@ void deg_graph_transitive_reduction(Depsgraph *graph) } } } + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_transitive.h b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h index a80a1d783d7..be9d7c3ca9c 100644 --- a/source/blender/depsgraph/util/depsgraph_util_transitive.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h @@ -24,15 +24,17 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_transitive.h +/** \file blender/depsgraph/intern/builder/deg_builder_transitive.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_TRANSITIVE_H__ -#define __DEPSGRAPH_UTIL_TRANSITIVE_H__ +#pragma once + +namespace DEG { struct Depsgraph; +/* Performs a transitive reduction to remove redundant relations. */ void deg_graph_transitive_reduction(Depsgraph *graph); -#endif /* __DEPSGRAPH_UTIL_TRANSITIVE_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc new file mode 100644 index 00000000000..5ce84ee29db --- /dev/null +++ b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc @@ -0,0 +1,588 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * Original Author: Lukas Toenne + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/debug/deg_debug_graphviz.cc + * \ingroup depsgraph + * + * Implementation of tools for debugging the depsgraph + */ + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +extern "C" { +#include "DNA_listBase.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_debug.h" +} /* extern "C" */ + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +/* ****************** */ +/* Graphviz Debugging */ + +namespace DEG { + +#define NL "\r\n" + +/* Only one should be enabled, defines whether graphviz nodes + * get colored by individual types or classes. + */ +#define COLOR_SCHEME_NODE_CLASS 1 +//#define COLOR_SCHEME_NODE_TYPE 2 + +static const char *deg_debug_graphviz_fontname = "helvetica"; +static float deg_debug_graphviz_graph_label_size = 20.0f; +static float deg_debug_graphviz_node_label_size = 14.0f; +static const int deg_debug_max_colors = 12; +#ifdef COLOR_SCHEME_NODE_TYPE +static const char *deg_debug_colors[] = { + "#a6cee3", "#1f78b4", "#b2df8a", + "#33a02c", "#fb9a99", "#e31a1c", + "#fdbf6f", "#ff7f00", "#cab2d6", + "#6a3d9a", "#ffff99", "#b15928", +}; +#endif +static const char *deg_debug_colors_light[] = { + "#8dd3c7", "#ffffb3", "#bebada", + "#fb8072", "#80b1d3", "#fdb462", + "#b3de69", "#fccde5", "#d9d9d9", + "#bc80bd", "#ccebc5", "#ffed6f", +}; + +#ifdef COLOR_SCHEME_NODE_TYPE +static const int deg_debug_node_type_color_map[][2] = { + {DEPSNODE_TYPE_ROOT, 0}, + {DEPSNODE_TYPE_TIMESOURCE, 1}, + {DEPSNODE_TYPE_ID_REF, 2}, + {DEPSNODE_TYPE_SUBGRAPH, 3}, + + /* Outer Types */ + {DEPSNODE_TYPE_PARAMETERS, 4}, + {DEPSNODE_TYPE_PROXY, 5}, + {DEPSNODE_TYPE_ANIMATION, 6}, + {DEPSNODE_TYPE_TRANSFORM, 7}, + {DEPSNODE_TYPE_GEOMETRY, 8}, + {DEPSNODE_TYPE_SEQUENCER, 9}, + {DEPSNODE_TYPE_SHADING, 10}, + {-1, 0} +}; +#endif + +static int deg_debug_node_color_index(const DepsNode *node) +{ +#ifdef COLOR_SCHEME_NODE_CLASS + /* Some special types. */ + switch (node->type) { + case DEPSNODE_TYPE_ID_REF: + return 5; + case DEPSNODE_TYPE_OPERATION: + { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->is_noop()) + return 8; + break; + } + + default: + break; + } + /* Do others based on class. */ + switch (node->tclass) { + case DEPSNODE_CLASS_OPERATION: + return 4; + case DEPSNODE_CLASS_COMPONENT: + return 1; + default: + return 9; + } +#endif + +#ifdef COLOR_SCHEME_NODE_TYPE + const int (*pair)[2]; + for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { + if ((*pair)[0] == node->type) { + return (*pair)[1]; + } + } + return -1; +#endif +} + +struct DebugContext { + FILE *file; + bool show_tags; + bool show_eval_priority; +}; + +static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3); +static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(ctx.file, fmt, args); + va_end(args); +} + +static void deg_debug_graphviz_legend_color(const DebugContext &ctx, + const char *name, + const char *color) +{ + deg_debug_fprintf(ctx, "<TR>"); + deg_debug_fprintf(ctx, "<TD>%s</TD>", name); + deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color); + deg_debug_fprintf(ctx, "</TR>" NL); +} + +static void deg_debug_graphviz_legend(const DebugContext &ctx) +{ + deg_debug_fprintf(ctx, "{" NL); + deg_debug_fprintf(ctx, "rank = sink;" NL); + deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL); + deg_debug_fprintf(ctx, " <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL); + deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL); + +#ifdef COLOR_SCHEME_NODE_CLASS + const char **colors = deg_debug_colors_light; + deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]); + deg_debug_graphviz_legend_color(ctx, "Component", colors[1]); + deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]); + deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]); +#endif + +#ifdef COLOR_SCHEME_NODE_TYPE + const int (*pair)[2]; + for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { + DepsNodeFactory *nti = DEG_get_node_factory((eDepsNode_Type)(*pair)[0]); + deg_debug_graphviz_legend_color(ctx, + nti->tname().c_str(), + deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]); + } +#endif + + deg_debug_fprintf(ctx, "</TABLE>" NL); + deg_debug_fprintf(ctx, ">" NL); + deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, "}" NL); +} + +static void deg_debug_graphviz_node_color(const DebugContext &ctx, + const DepsNode *node) +{ + const char *color_default = "black"; + const char *color_modified = "orangered4"; + const char *color_update = "dodgerblue3"; + const char *color = color_default; + if (ctx.show_tags) { + if (node->tclass == DEPSNODE_CLASS_OPERATION) { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { + color = color_modified; + } + else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { + color = color_update; + } + } + } + deg_debug_fprintf(ctx, "\"%s\"", color); +} + +static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx, + const DepsNode *node) +{ + float penwidth_default = 1.0f; + float penwidth_modified = 4.0f; + float penwidth_update = 4.0f; + float penwidth = penwidth_default; + if (ctx.show_tags) { + if (node->tclass == DEPSNODE_CLASS_OPERATION) { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { + penwidth = penwidth_modified; + } + else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { + penwidth = penwidth_update; + } + } + } + deg_debug_fprintf(ctx, "\"%f\"", penwidth); +} + +static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx, + const DepsNode *node) +{ + const char *defaultcolor = "gainsboro"; + int color_index = deg_debug_node_color_index(node); + const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors]; + deg_debug_fprintf(ctx, "\"%s\"", fillcolor); +} + +static void deg_debug_graphviz_relation_color(const DebugContext &ctx, + const DepsRelation *rel) +{ + const char *color_default = "black"; + const char *color_error = "red4"; + const char *color = color_default; + if (rel->flag & DEPSREL_FLAG_CYCLIC) { + color = color_error; + } + deg_debug_fprintf(ctx, "%s", color); +} + +static void deg_debug_graphviz_node_style(const DebugContext &ctx, const DepsNode *node) +{ + const char *base_style = "filled"; /* default style */ + if (ctx.show_tags) { + if (node->tclass == DEPSNODE_CLASS_OPERATION) { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) { + base_style = "striped"; + } + } + } + switch (node->tclass) { + case DEPSNODE_CLASS_GENERIC: + deg_debug_fprintf(ctx, "\"%s\"", base_style); + break; + case DEPSNODE_CLASS_COMPONENT: + deg_debug_fprintf(ctx, "\"%s\"", base_style); + break; + case DEPSNODE_CLASS_OPERATION: + deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style); + break; + } +} + +static void deg_debug_graphviz_node_single(const DebugContext &ctx, + const DepsNode *node) +{ + const char *shape = "box"; + string name = node->identifier(); + float priority = -1.0f; + if (node->type == DEPSNODE_TYPE_ID_REF) { + IDDepsNode *id_node = (IDDepsNode *)node; + char buf[256]; + BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); + name += buf; + } + if (ctx.show_eval_priority && node->tclass == DEPSNODE_CLASS_OPERATION) { + priority = ((OperationDepsNode *)node)->eval_priority; + } + deg_debug_fprintf(ctx, "// %s\n", name.c_str()); + deg_debug_fprintf(ctx, "\"node_%p\"", node); + deg_debug_fprintf(ctx, "["); +// deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name); + if (priority >= 0.0f) { + deg_debug_fprintf(ctx, "label=<%s<BR/>(<I>%.2f</I>)>", + name.c_str(), + priority); + } + else { + deg_debug_fprintf(ctx, "label=<%s>", name.c_str()); + } + deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); + deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size); + deg_debug_fprintf(ctx, ",shape=%s", shape); + deg_debug_fprintf(ctx, ",style="); deg_debug_graphviz_node_style(ctx, node); + deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_node_color(ctx, node); + deg_debug_fprintf(ctx, ",fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); + deg_debug_fprintf(ctx, ",penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, NL); +} + +static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx, + const DepsNode *node) +{ + string name = node->identifier().c_str(); + if (node->type == DEPSNODE_TYPE_ID_REF) { + IDDepsNode *id_node = (IDDepsNode *)node; + char buf[256]; + BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); + name += buf; + } + deg_debug_fprintf(ctx, "// %s\n", name.c_str()); + deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node); +// deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name); + deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str()); + deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname); + deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size); + deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16); + deg_debug_fprintf(ctx, "style="); deg_debug_graphviz_node_style(ctx, node); deg_debug_fprintf(ctx, ";" NL); + deg_debug_fprintf(ctx, "color="); deg_debug_graphviz_node_color(ctx, node); deg_debug_fprintf(ctx, ";" NL); + deg_debug_fprintf(ctx, "fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); deg_debug_fprintf(ctx, ";" NL); + deg_debug_fprintf(ctx, "penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); deg_debug_fprintf(ctx, ";" NL); + /* dummy node, so we can add edges between clusters */ + deg_debug_fprintf(ctx, "\"node_%p\"", node); + deg_debug_fprintf(ctx, "["); + deg_debug_fprintf(ctx, "shape=%s", "point"); + deg_debug_fprintf(ctx, ",style=%s", "invis"); + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, NL); +} + +static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx) +{ + deg_debug_fprintf(ctx, "}" NL); + deg_debug_fprintf(ctx, NL); +} + +static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, + const Depsgraph *graph); +static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, + const Depsgraph *graph); + +static void deg_debug_graphviz_node(const DebugContext &ctx, + const DepsNode *node) +{ + switch (node->type) { + case DEPSNODE_TYPE_ID_REF: + { + const IDDepsNode *id_node = (const IDDepsNode *)node; + if (BLI_ghash_size(id_node->components) == 0) { + deg_debug_graphviz_node_single(ctx, node); + } + else { + deg_debug_graphviz_node_cluster_begin(ctx, node); + GHASH_FOREACH_BEGIN(const ComponentDepsNode *, comp, id_node->components) + { + deg_debug_graphviz_node(ctx, comp); + } + GHASH_FOREACH_END(); + deg_debug_graphviz_node_cluster_end(ctx); + } + break; + } + case DEPSNODE_TYPE_SUBGRAPH: + { + SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; + if (sub_node->graph) { + deg_debug_graphviz_node_cluster_begin(ctx, node); + deg_debug_graphviz_graph_nodes(ctx, sub_node->graph); + deg_debug_graphviz_node_cluster_end(ctx); + } + else { + deg_debug_graphviz_node_single(ctx, node); + } + break; + } + case DEPSNODE_TYPE_PARAMETERS: + case DEPSNODE_TYPE_ANIMATION: + case DEPSNODE_TYPE_TRANSFORM: + case DEPSNODE_TYPE_PROXY: + case DEPSNODE_TYPE_GEOMETRY: + case DEPSNODE_TYPE_SEQUENCER: + case DEPSNODE_TYPE_EVAL_POSE: + case DEPSNODE_TYPE_BONE: + case DEPSNODE_TYPE_SHADING: + case DEPSNODE_TYPE_EVAL_PARTICLES: + { + ComponentDepsNode *comp_node = (ComponentDepsNode *)node; + if (!comp_node->operations.empty()) { + deg_debug_graphviz_node_cluster_begin(ctx, node); + foreach (DepsNode *op_node, comp_node->operations) { + deg_debug_graphviz_node(ctx, op_node); + } + deg_debug_graphviz_node_cluster_end(ctx); + } + else { + deg_debug_graphviz_node_single(ctx, node); + } + break; + } + default: + deg_debug_graphviz_node_single(ctx, node); + break; + } +} + +static bool deg_debug_graphviz_is_cluster(const DepsNode *node) +{ + switch (node->type) { + case DEPSNODE_TYPE_ID_REF: + { + const IDDepsNode *id_node = (const IDDepsNode *)node; + return BLI_ghash_size(id_node->components) > 0; + } + case DEPSNODE_TYPE_SUBGRAPH: + { + SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; + return sub_node->graph != NULL; + } + case DEPSNODE_TYPE_PARAMETERS: + case DEPSNODE_TYPE_ANIMATION: + case DEPSNODE_TYPE_TRANSFORM: + case DEPSNODE_TYPE_PROXY: + case DEPSNODE_TYPE_GEOMETRY: + case DEPSNODE_TYPE_SEQUENCER: + case DEPSNODE_TYPE_EVAL_POSE: + case DEPSNODE_TYPE_BONE: + { + ComponentDepsNode *comp_node = (ComponentDepsNode *)node; + return !comp_node->operations.empty(); + } + default: + return false; + } +} + +static bool deg_debug_graphviz_is_owner(const DepsNode *node, + const DepsNode *other) +{ + switch (node->tclass) { + case DEPSNODE_CLASS_COMPONENT: + { + ComponentDepsNode *comp_node = (ComponentDepsNode *)node; + if (comp_node->owner == other) + return true; + break; + } + case DEPSNODE_CLASS_OPERATION: + { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->owner == other) + return true; + else if (op_node->owner->owner == other) + return true; + break; + } + default: break; + } + return false; +} + +static void deg_debug_graphviz_node_relations(const DebugContext &ctx, + const DepsNode *node) +{ + foreach (DepsRelation *rel, node->inlinks) { + float penwidth = 2.0f; + + const DepsNode *tail = rel->to; /* same as node */ + const DepsNode *head = rel->from; + deg_debug_fprintf(ctx, "// %s -> %s\n", + head->identifier().c_str(), + tail->identifier().c_str()); + deg_debug_fprintf(ctx, "\"node_%p\"", head); + deg_debug_fprintf(ctx, " -> "); + deg_debug_fprintf(ctx, "\"node_%p\"", tail); + + deg_debug_fprintf(ctx, "["); + /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */ + deg_debug_fprintf(ctx, "id=\"%s\"", rel->name); + deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_relation_color(ctx, rel); + deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth); + /* NOTE: edge from node to own cluster is not possible and gives graphviz + * warning, avoid this here by just linking directly to the invisible + * placeholder node + */ + if (deg_debug_graphviz_is_cluster(tail) && !deg_debug_graphviz_is_owner(head, tail)) { + deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail); + } + if (deg_debug_graphviz_is_cluster(head) && !deg_debug_graphviz_is_owner(tail, head)) { + deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head); + } + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, NL); + } +} + +static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, + const Depsgraph *graph) +{ + if (graph->root_node) { + deg_debug_graphviz_node(ctx, graph->root_node); + } + GHASH_FOREACH_BEGIN (DepsNode *, node, graph->id_hash) + { + deg_debug_graphviz_node(ctx, node); + } + GHASH_FOREACH_END(); + TimeSourceDepsNode *time_source = graph->find_time_source(NULL); + if (time_source != NULL) { + deg_debug_graphviz_node(ctx, time_source); + } +} + +static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, + const Depsgraph *graph) +{ + GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) + { + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components) + { + foreach (OperationDepsNode *op_node, comp_node->operations) { + deg_debug_graphviz_node_relations(ctx, op_node); + } + } + GHASH_FOREACH_END(); + } + GHASH_FOREACH_END(); + + TimeSourceDepsNode *time_source = graph->find_time_source(NULL); + if (time_source != NULL) { + deg_debug_graphviz_node_relations(ctx, time_source); + } +} + +} // namespace DEG + +void DEG_debug_graphviz(const Depsgraph *graph, FILE *f, const char *label, bool show_eval) +{ + if (!graph) { + return; + } + + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + + DEG::DebugContext ctx; + ctx.file = f; + ctx.show_tags = show_eval; + ctx.show_eval_priority = show_eval; + + DEG::deg_debug_fprintf(ctx, "digraph depgraph {" NL); + DEG::deg_debug_fprintf(ctx, "rankdir=LR;" NL); + DEG::deg_debug_fprintf(ctx, "graph ["); + DEG::deg_debug_fprintf(ctx, "compound=true"); + DEG::deg_debug_fprintf(ctx, ",labelloc=\"t\""); + DEG::deg_debug_fprintf(ctx, ",fontsize=%f", DEG::deg_debug_graphviz_graph_label_size); + DEG::deg_debug_fprintf(ctx, ",fontname=\"%s\"", DEG::deg_debug_graphviz_fontname); + DEG::deg_debug_fprintf(ctx, ",label=\"%s\"", label); + DEG::deg_debug_fprintf(ctx, ",splines=ortho"); + DEG::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato + DEG::deg_debug_fprintf(ctx, "];" NL); + + DEG::deg_debug_graphviz_graph_nodes(ctx, deg_graph); + DEG::deg_debug_graphviz_graph_relations(ctx, deg_graph); + + DEG::deg_debug_graphviz_legend(ctx); + + DEG::deg_debug_fprintf(ctx, "}" NL); +} + +#undef NL diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index d293d03f63d..2b7c63767ab 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -30,10 +30,14 @@ * Core routines for how the Depsgraph works. */ +#include "intern/depsgraph.h" /* own include */ + #include <string.h> #include "MEM_guardedalloc.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "BLI_listbase.h" extern "C" { @@ -50,11 +54,15 @@ extern "C" { } #include "DEG_depsgraph.h" -#include "depsgraph.h" /* own include */ -#include "depsnode.h" -#include "depsnode_operation.h" -#include "depsnode_component.h" -#include "depsgraph_intern.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +namespace DEG { static DEG_EditorUpdateIDCb deg_editor_update_id_cb = NULL; static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = NULL; @@ -66,6 +74,9 @@ Depsgraph::Depsgraph() layers(0) { BLI_spin_init(&lock); + id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); + subgraphs = BLI_gset_ptr_new("Depsgraph subgraphs"); + entry_tags = BLI_gset_ptr_new("Depsgraph entry_tags"); } Depsgraph::~Depsgraph() @@ -73,6 +84,9 @@ Depsgraph::~Depsgraph() /* Free root node - it won't have been freed yet... */ clear_id_nodes(); clear_subgraph_nodes(); + BLI_ghash_free(id_hash, NULL, NULL); + BLI_gset_free(subgraphs, NULL); + BLI_gset_free(entry_tags, NULL); if (this->root_node != NULL) { OBJECT_GUARDED_DELETE(this->root_node, RootDepsNode); } @@ -235,10 +249,16 @@ DepsNode *Depsgraph::find_node_from_pointer(const PointerRNA *ptr, /* Node Management ---------------------------- */ +static void id_node_deleter(void *value) +{ + IDDepsNode *id_node = reinterpret_cast<IDDepsNode *>(value); + OBJECT_GUARDED_DELETE(id_node, IDDepsNode); +} + RootDepsNode *Depsgraph::add_root_node() { if (!root_node) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_ROOT); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_ROOT); root_node = (RootDepsNode *)factory->create_node(NULL, "", "Root (Scene)"); } return root_node; @@ -267,12 +287,12 @@ TimeSourceDepsNode *Depsgraph::find_time_source(const ID *id) const SubgraphDepsNode *Depsgraph::add_subgraph_node(const ID *id) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_SUBGRAPH); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_SUBGRAPH); SubgraphDepsNode *subgraph_node = (SubgraphDepsNode *)factory->create_node(id, "", id->name + 2); /* Add to subnodes list. */ - this->subgraphs.insert(subgraph_node); + BLI_gset_insert(subgraphs, subgraph_node); /* if there's an ID associated, add to ID-nodes lookup too */ if (id) { @@ -289,37 +309,34 @@ SubgraphDepsNode *Depsgraph::add_subgraph_node(const ID *id) void Depsgraph::remove_subgraph_node(SubgraphDepsNode *subgraph_node) { - subgraphs.erase(subgraph_node); + BLI_gset_remove(subgraphs, subgraph_node, NULL); OBJECT_GUARDED_DELETE(subgraph_node, SubgraphDepsNode); } void Depsgraph::clear_subgraph_nodes() { - for (Subgraphs::iterator it = subgraphs.begin(); - it != subgraphs.end(); - ++it) + GSET_FOREACH_BEGIN(SubgraphDepsNode *, subgraph_node, subgraphs) { - SubgraphDepsNode *subgraph_node = *it; OBJECT_GUARDED_DELETE(subgraph_node, SubgraphDepsNode); } - subgraphs.clear(); + GSET_FOREACH_END(); + BLI_gset_clear(subgraphs, NULL); } IDDepsNode *Depsgraph::find_id_node(const ID *id) const { - IDNodeMap::const_iterator it = this->id_hash.find(id); - return it != this->id_hash.end() ? it->second : NULL; + return reinterpret_cast<IDDepsNode *>(BLI_ghash_lookup(id_hash, id)); } IDDepsNode *Depsgraph::add_id_node(ID *id, const string &name) { IDDepsNode *id_node = find_id_node(id); if (!id_node) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_ID_REF); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_ID_REF); id_node = (IDDepsNode *)factory->create_node(id, "", name); id->tag |= LIB_TAG_DOIT; /* register */ - this->id_hash[id] = id_node; + BLI_ghash_insert(id_hash, id, id_node); } return id_node; } @@ -329,21 +346,14 @@ void Depsgraph::remove_id_node(const ID *id) IDDepsNode *id_node = find_id_node(id); if (id_node) { /* unregister */ - this->id_hash.erase(id); + BLI_ghash_remove(id_hash, id, NULL, NULL); OBJECT_GUARDED_DELETE(id_node, IDDepsNode); } } void Depsgraph::clear_id_nodes() { - for (IDNodeMap::const_iterator it = id_hash.begin(); - it != id_hash.end(); - ++it) - { - IDDepsNode *id_node = it->second; - OBJECT_GUARDED_DELETE(id_node, IDDepsNode); - } - id_hash.clear(); + BLI_ghash_clear(id_hash, NULL, id_node_deleter); } /* Add new relationship between two nodes. */ @@ -449,33 +459,52 @@ void Depsgraph::add_entry_tag(OperationDepsNode *node) /* Add to graph-level set of directly modified nodes to start searching from. * NOTE: this is necessary since we have several thousand nodes to play with... */ - this->entry_tags.insert(node); + BLI_gset_insert(entry_tags, node); } void Depsgraph::clear_all_nodes() { clear_id_nodes(); clear_subgraph_nodes(); - id_hash.clear(); + BLI_ghash_clear(id_hash, NULL, NULL); if (this->root_node) { OBJECT_GUARDED_DELETE(this->root_node, RootDepsNode); root_node = NULL; } } +void deg_editors_id_update(Main *bmain, ID *id) +{ + if (deg_editor_update_id_cb != NULL) { + deg_editor_update_id_cb(bmain, id); + } +} + +void deg_editors_scene_update(Main *bmain, Scene *scene, bool updated) +{ + if (deg_editor_update_scene_cb != NULL) { + deg_editor_update_scene_cb(bmain, scene, updated); + } +} + +} // namespace DEG + /* **************** */ /* Public Graph API */ /* Initialize a new Depsgraph */ Depsgraph *DEG_graph_new() { - return OBJECT_GUARDED_NEW(Depsgraph); + DEG::Depsgraph *deg_depsgraph = OBJECT_GUARDED_NEW(DEG::Depsgraph); + return reinterpret_cast<Depsgraph *>(deg_depsgraph); } /* Free graph's contents and graph itself */ void DEG_graph_free(Depsgraph *graph) { - OBJECT_GUARDED_DELETE(graph, Depsgraph); + using DEG::Depsgraph; + DEG::Depsgraph *deg_depsgraph = reinterpret_cast<DEG::Depsgraph *>(graph); + OBJECT_GUARDED_DELETE(deg_depsgraph, Depsgraph); } /* Set callbacks which are being called when depsgraph changes. */ @@ -483,28 +512,14 @@ void DEG_editors_set_update_cb(DEG_EditorUpdateIDCb id_func, DEG_EditorUpdateSceneCb scene_func, DEG_EditorUpdateScenePreCb scene_pre_func) { - deg_editor_update_id_cb = id_func; - deg_editor_update_scene_cb = scene_func; - deg_editor_update_scene_pre_cb = scene_pre_func; + DEG::deg_editor_update_id_cb = id_func; + DEG::deg_editor_update_scene_cb = scene_func; + DEG::deg_editor_update_scene_pre_cb = scene_pre_func; } void DEG_editors_update_pre(Main *bmain, Scene *scene, bool time) { - if (deg_editor_update_scene_pre_cb != NULL) { - deg_editor_update_scene_pre_cb(bmain, scene, time); - } -} - -void deg_editors_id_update(Main *bmain, ID *id) -{ - if (deg_editor_update_id_cb != NULL) { - deg_editor_update_id_cb(bmain, id); - } -} - -void deg_editors_scene_update(Main *bmain, Scene *scene, bool updated) -{ - if (deg_editor_update_scene_cb != NULL) { - deg_editor_update_scene_cb(bmain, scene, updated); + if (DEG::deg_editor_update_scene_pre_cb != NULL) { + DEG::deg_editor_update_scene_pre_cb(bmain, scene, time); } } diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index 9533fbd10d5..213bb304d73 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -34,19 +34,20 @@ * in the graph. */ -#ifndef __DEPSGRAPH_H__ -#define __DEPSGRAPH_H__ +#pragma once #include "BLI_threads.h" /* for SpinLock */ -#include "depsgraph_types.h" - -#include "depsgraph_util_map.h" -#include "depsgraph_util_set.h" +#include "intern/depsgraph_types.h" +struct ID; +struct GHash; +struct GSet; struct PointerRNA; struct PropertyRNA; +namespace DEG { + struct DepsNode; struct RootDepsNode; struct TimeSourceDepsNode; @@ -94,9 +95,6 @@ struct DepsRelation { /* Dependency Graph object */ struct Depsgraph { - typedef unordered_map<const ID *, IDDepsNode *> IDNodeMap; - typedef unordered_set<SubgraphDepsNode *> Subgraphs; - typedef unordered_set<OperationDepsNode *> EntryTags; typedef vector<OperationDepsNode *> OperationNodes; Depsgraph(); @@ -163,13 +161,13 @@ struct Depsgraph { /* <ID : IDDepsNode> mapping from ID blocks to nodes representing these blocks * (for quick lookups). */ - IDNodeMap id_hash; + GHash *id_hash; /* "root" node - the one where all evaluation enters from. */ RootDepsNode *root_node; /* Subgraphs referenced in tree. */ - Subgraphs subgraphs; + GSet *subgraphs; /* Indicates whether relations needs to be updated. */ bool need_update; @@ -177,7 +175,7 @@ struct Depsgraph { /* Quick-Access Temp Data ............. */ /* Nodes which have been tagged as "directly modified". */ - EntryTags entry_tags; + GSet *entry_tags; /* Convenience Data ................... */ @@ -198,27 +196,4 @@ struct Depsgraph { // XXX: additional stuff like eval contexts, mempools for allocating nodes from, etc. }; -/** - * Helper macros for iterating over set of relationship links - * incident on each node. - * - * \note it is safe to perform removal operations here... - * - * relations_set[in]: (DepsNode::Relations) set of relationships (in/out links) - * relation[out]: (DepsRelation *) identifier where DepsRelation that we're - * currently accessing comes up - */ -#define DEPSNODE_RELATIONS_ITER_BEGIN(relations_set_, relation_) \ - { \ - OperationDepsNode::Relations::const_iterator __rel_iter = relations_set_.begin(); \ - while (__rel_iter != relations_set_.end()) { \ - DepsRelation *relation_ = *__rel_iter; \ - ++__rel_iter; \ - - /* ... code for iterator body can be written here ... */ - -#define DEPSNODE_RELATIONS_ITER_END \ - } \ - } ((void)0) - -#endif /* __DEPSGRAPH_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 877ce6e52e9..b1271c39851 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -30,137 +30,128 @@ * Methods for constructing depsgraph. */ -#include <stack> - #include "MEM_guardedalloc.h" extern "C" { -#include "BLI_blenlib.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "DNA_action_types.h" -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_camera_types.h" -#include "DNA_constraint_types.h" -#include "DNA_curve_types.h" -#include "DNA_effect_types.h" -#include "DNA_group_types.h" -#include "DNA_key_types.h" -#include "DNA_lamp_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_node_types.h" -#include "DNA_particle_types.h" #include "DNA_object_types.h" -#include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" -#include "DNA_texture_types.h" -#include "DNA_world_types.h" - -#include "BKE_action.h" -#include "BKE_armature.h" -#include "BKE_animsys.h" -#include "BKE_constraint.h" -#include "BKE_curve.h" -#include "BKE_effect.h" -#include "BKE_fcurve.h" -#include "BKE_group.h" -#include "BKE_key.h" -#include "BKE_library.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + #include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_mball.h" -#include "BKE_modifier.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_particle.h" -#include "BKE_rigidbody.h" -#include "BKE_sound.h" -#include "BKE_texture.h" -#include "BKE_tracking.h" -#include "BKE_world.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" #include "DEG_depsgraph_build.h" -#include "RNA_access.h" -#include "RNA_types.h" } /* extern "C" */ -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsgraph_debug.h" -#include "depsnode_operation.h" -#include "depsgraph_types.h" -#include "depsgraph_build.h" -#include "depsgraph_intern.h" +#include "builder/deg_builder.h" +#include "builder/deg_builder_cycle.h" +#include "builder/deg_builder_nodes.h" +#include "builder/deg_builder_relations.h" +#include "builder/deg_builder_transitive.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_types.h" +#include "intern/depsgraph_intern.h" -#include "depsgraph_util_cycle.h" -#include "depsgraph_util_transitive.h" +#include "util/deg_util_foreach.h" /* ****************** */ /* External Build API */ -static eDepsNode_Type deg_build_scene_component_type(eDepsSceneComponentType component) +static DEG::eDepsNode_Type deg_build_scene_component_type( + eDepsSceneComponentType component) { switch (component) { - case DEG_SCENE_COMP_PARAMETERS: return DEPSNODE_TYPE_PARAMETERS; - case DEG_SCENE_COMP_ANIMATION: return DEPSNODE_TYPE_ANIMATION; - case DEG_SCENE_COMP_SEQUENCER: return DEPSNODE_TYPE_SEQUENCER; + case DEG_SCENE_COMP_PARAMETERS: return DEG::DEPSNODE_TYPE_PARAMETERS; + case DEG_SCENE_COMP_ANIMATION: return DEG::DEPSNODE_TYPE_ANIMATION; + case DEG_SCENE_COMP_SEQUENCER: return DEG::DEPSNODE_TYPE_SEQUENCER; } - return DEPSNODE_TYPE_UNDEFINED; + return DEG::DEPSNODE_TYPE_UNDEFINED; } -static eDepsNode_Type deg_build_object_component_type(eDepsObjectComponentType component) +static DEG::eDepsNode_Type deg_build_object_component_type( + eDepsObjectComponentType component) { switch (component) { - case DEG_OB_COMP_PARAMETERS: return DEPSNODE_TYPE_PARAMETERS; - case DEG_OB_COMP_PROXY: return DEPSNODE_TYPE_PROXY; - case DEG_OB_COMP_ANIMATION: return DEPSNODE_TYPE_ANIMATION; - case DEG_OB_COMP_TRANSFORM: return DEPSNODE_TYPE_TRANSFORM; - case DEG_OB_COMP_GEOMETRY: return DEPSNODE_TYPE_GEOMETRY; - case DEG_OB_COMP_EVAL_POSE: return DEPSNODE_TYPE_EVAL_POSE; - case DEG_OB_COMP_BONE: return DEPSNODE_TYPE_BONE; - case DEG_OB_COMP_EVAL_PARTICLES: return DEPSNODE_TYPE_EVAL_PARTICLES; - case DEG_OB_COMP_SHADING: return DEPSNODE_TYPE_SHADING; + case DEG_OB_COMP_PARAMETERS: return DEG::DEPSNODE_TYPE_PARAMETERS; + case DEG_OB_COMP_PROXY: return DEG::DEPSNODE_TYPE_PROXY; + case DEG_OB_COMP_ANIMATION: return DEG::DEPSNODE_TYPE_ANIMATION; + case DEG_OB_COMP_TRANSFORM: return DEG::DEPSNODE_TYPE_TRANSFORM; + case DEG_OB_COMP_GEOMETRY: return DEG::DEPSNODE_TYPE_GEOMETRY; + case DEG_OB_COMP_EVAL_POSE: return DEG::DEPSNODE_TYPE_EVAL_POSE; + case DEG_OB_COMP_BONE: return DEG::DEPSNODE_TYPE_BONE; + case DEG_OB_COMP_EVAL_PARTICLES: return DEG::DEPSNODE_TYPE_EVAL_PARTICLES; + case DEG_OB_COMP_SHADING: return DEG::DEPSNODE_TYPE_SHADING; } - return DEPSNODE_TYPE_UNDEFINED; + return DEG::DEPSNODE_TYPE_UNDEFINED; } -void DEG_add_scene_relation(DepsNodeHandle *handle, struct Scene *scene, eDepsSceneComponentType component, const char *description) +static DEG::DepsNodeHandle *get_handle(DepsNodeHandle *handle) { - eDepsNode_Type type = deg_build_scene_component_type(component); - ComponentKey comp_key(&scene->id, type); - handle->builder->add_node_handle_relation(comp_key, handle, DEPSREL_TYPE_GEOMETRY_EVAL, description); + return reinterpret_cast<DEG::DepsNodeHandle *>(handle); } -void DEG_add_object_relation(DepsNodeHandle *handle, struct Object *ob, eDepsObjectComponentType component, const char *description) +void DEG_add_scene_relation(DepsNodeHandle *handle, + Scene *scene, + eDepsSceneComponentType component, + const char *description) { - eDepsNode_Type type = deg_build_object_component_type(component); - ComponentKey comp_key(&ob->id, type); - handle->builder->add_node_handle_relation(comp_key, handle, DEPSREL_TYPE_GEOMETRY_EVAL, description); + DEG::eDepsNode_Type type = deg_build_scene_component_type(component); + DEG::ComponentKey comp_key(&scene->id, type); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_GEOMETRY_EVAL, + description); } -void DEG_add_bone_relation(DepsNodeHandle *handle, struct Object *ob, const char *bone_name, eDepsObjectComponentType component, const char *description) +void DEG_add_object_relation(DepsNodeHandle *handle, + Object *ob, + eDepsObjectComponentType component, + const char *description) { - eDepsNode_Type type = deg_build_object_component_type(component); - ComponentKey comp_key(&ob->id, type, bone_name); + DEG::eDepsNode_Type type = deg_build_object_component_type(component); + DEG::ComponentKey comp_key(&ob->id, type); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_GEOMETRY_EVAL, + description); +} - // XXX: "Geometry Eval" might not always be true, but this only gets called from modifier building now - handle->builder->add_node_handle_relation(comp_key, handle, DEPSREL_TYPE_GEOMETRY_EVAL, description); +void DEG_add_bone_relation(DepsNodeHandle *handle, + Object *ob, + const char *bone_name, + eDepsObjectComponentType component, + const char *description) +{ + DEG::eDepsNode_Type type = deg_build_object_component_type(component); + DEG::ComponentKey comp_key(&ob->id, type, bone_name); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + /* XXX: "Geometry Eval" might not always be true, but this only gets called + * from modifier building now. + */ + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_GEOMETRY_EVAL, + description); } void DEG_add_special_eval_flag(Depsgraph *graph, ID *id, short flag) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); if (graph == NULL) { BLI_assert(!"Graph should always be valid"); return; } - IDDepsNode *id_node = graph->find_id_node(id); + DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); if (id_node == NULL) { BLI_assert(!"ID should always be valid"); return; @@ -168,110 +159,21 @@ void DEG_add_special_eval_flag(Depsgraph *graph, ID *id, short flag) id_node->eval_flags |= flag; } -/* ********************** */ -/* Utilities for Builders */ - -/* Get unique identifier for FCurves and Drivers */ -string deg_fcurve_id_name(const FCurve *fcu) -{ - char index_buf[32]; - sprintf(index_buf, "[%d]", fcu->array_index); - - return string(fcu->rna_path) + index_buf; -} - -static void deg_graph_build_finalize(Depsgraph *graph) -{ - std::stack<OperationDepsNode *> stack; - - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; - node->done = 0; - node->num_links_pending = 0; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if ((rel->from->type == DEPSNODE_TYPE_OPERATION) && - (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) - { - ++node->num_links_pending; - } - } - if (node->num_links_pending == 0) { - stack.push(node); - } - IDDepsNode *id_node = node->owner->owner; - id_node->id->tag |= LIB_TAG_DOIT; - } - - while (!stack.empty()) { - OperationDepsNode *node = stack.top(); - if (node->done == 0 && node->outlinks.size() != 0) { - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - BLI_assert(to->num_links_pending > 0); - --to->num_links_pending; - } - if (to->num_links_pending == 0) { - stack.push(to); - } - } - } - node->done = 1; - } - else { - stack.pop(); - IDDepsNode *id_node = node->owner->owner; - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; - IDDepsNode *id_to = to->owner->owner; - id_node->layers |= id_to->layers; - } - } - } - } - - /* Re-tag IDs for update if it was tagged before the relations update tag. */ - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) - { - IDDepsNode *id_node = it->second; - ID *id = id_node->id; - if (id->tag & LIB_TAG_ID_RECALC_ALL && - id->tag & LIB_TAG_DOIT) - { - id_node->tag_update(graph); - id->tag &= ~LIB_TAG_DOIT; - } - } -} - /* ******************** */ /* Graph Building API's */ -/* Build depsgraph for the given scene, and dump results in given graph container */ -// XXX: assume that this is called from outside, given the current scene as the "main" scene +/* Build depsgraph for the given scene, and dump results in given + * graph container. + */ +/* XXX: assume that this is called from outside, given the current scene as + * the "main" scene. + */ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + /* 1) Generate all the nodes in the graph first */ - DepsgraphNodeBuilder node_builder(bmain, graph); + DEG::DepsgraphNodeBuilder node_builder(bmain, deg_graph); /* create root node for scene first * - this way it should be the first in the graph, * reflecting its role as the entrypoint @@ -279,29 +181,40 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) node_builder.add_root_node(); node_builder.build_scene(bmain, scene); - /* 2) Hook up relationships between operations - to determine evaluation order */ - DepsgraphRelationBuilder relation_builder(graph); - /* hook scene up to the root node as entrypoint to graph */ + /* 2) Hook up relationships between operations - to determine evaluation + * order. + */ + DEG::DepsgraphRelationBuilder relation_builder(deg_graph); + /* Hook scene up to the root node as entrypoint to graph. */ /* XXX what does this relation actually mean? - * it doesnt add any operations anyway and is not clear what part of the scene is to be connected. + * it doesnt add any operations anyway and is not clear what part of the + * scene is to be connected. */ - //relation_builder.add_relation(RootKey(), IDKey(scene), DEPSREL_TYPE_ROOT_TO_ACTIVE, "Root to Active Scene"); +#if 0 + relation_builder.add_relation(RootKey(), + IDKey(scene), + DEPSREL_TYPE_ROOT_TO_ACTIVE, + "Root to Active Scene"); +#endif relation_builder.build_scene(bmain, scene); /* Detect and solve cycles. */ - deg_graph_detect_cycles(graph); + DEG::deg_graph_detect_cycles(deg_graph); - /* 3) Simplify the graph by removing redundant relations (to optimise traversal later) */ - // TODO: it would be useful to have an option to disable this in cases where it is causing trouble + /* 3) Simplify the graph by removing redundant relations (to optimize + * traversal later). */ + /* TODO: it would be useful to have an option to disable this in cases where + * it is causing trouble. + */ if (G.debug_value == 799) { - deg_graph_transitive_reduction(graph); + DEG::deg_graph_transitive_reduction(deg_graph); } /* 4) Flush visibility layer and re-schedule nodes for update. */ - deg_graph_build_finalize(graph); + DEG::deg_graph_build_finalize(deg_graph); #if 0 - if (!DEG_debug_consistency_check(graph)) { + if (!DEG_debug_consistency_check(deg_graph)) { printf("Consistency validation failed, ABORTING!\n"); abort(); } @@ -311,7 +224,8 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) /* Tag graph relations for update. */ void DEG_graph_tag_relations_update(Depsgraph *graph) { - graph->need_update = true; + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + deg_graph->need_update = true; } /* Tag all relations for update. */ @@ -339,7 +253,7 @@ void DEG_scene_relations_update(Main *bmain, Scene *scene) return; } - Depsgraph *graph = scene->depsgraph; + DEG::Depsgraph *graph = reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph); if (!graph->need_update) { /* Graph is up to date, nothing to do. */ return; @@ -348,10 +262,12 @@ void DEG_scene_relations_update(Main *bmain, Scene *scene) /* Clear all previous nodes and operations. */ graph->clear_all_nodes(); graph->operations.clear(); - graph->entry_tags.clear(); + BLI_gset_clear(graph->entry_tags, NULL); /* Build new nodes and relations. */ - DEG_graph_build_from_scene(graph, bmain, scene); + DEG_graph_build_from_scene(reinterpret_cast< ::Depsgraph * >(graph), + bmain, + scene); graph->need_update = false; } diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index efb3e330857..d3b48930779 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -30,956 +30,39 @@ * Implementation of tools for debugging the depsgraph */ -//#include <stdlib.h> -#include <string.h> - -extern "C" { #include "BLI_utildefines.h" -#include "BLI_listbase.h" #include "BLI_ghash.h" -#include "BLI_string.h" +extern "C" { #include "DNA_scene_types.h" -#include "DNA_userdef_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" #include "DEG_depsgraph_build.h" - -#include "WM_api.h" -#include "WM_types.h" } /* extern "C" */ -#include "depsgraph_debug.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" - -/* ****************** */ -/* Graphviz Debugging */ - -#define NL "\r\n" - -/* Only one should be enabled, defines whether graphviz nodes - * get colored by individual types or classes. - */ -#define COLOR_SCHEME_NODE_CLASS 1 -//#define COLOR_SCHEME_NODE_TYPE 2 - -static const char *deg_debug_graphviz_fontname = "helvetica"; -static float deg_debug_graphviz_graph_label_size = 20.0f; -static float deg_debug_graphviz_node_label_size = 14.0f; -static const int deg_debug_max_colors = 12; -#if 0 -static const char *deg_debug_colors_dark[] = { - "#6e8997", "#144f77", "#76945b", - "#216a1d", "#a76665", "#971112", - "#a87f49", "#0a9540", "#86768e", - "#462866", "#a9a965", "#753b1a", -}; -#endif -#ifdef COLOR_SCHEME_NODE_TYPE -static const char *deg_debug_colors[] = { - "#a6cee3", "#1f78b4", "#b2df8a", - "#33a02c", "#fb9a99", "#e31a1c", - "#fdbf6f", "#ff7f00", "#cab2d6", - "#6a3d9a", "#ffff99", "#b15928", -}; -#endif -static const char *deg_debug_colors_light[] = { - "#8dd3c7", "#ffffb3", "#bebada", - "#fb8072", "#80b1d3", "#fdb462", - "#b3de69", "#fccde5", "#d9d9d9", - "#bc80bd", "#ccebc5", "#ffed6f", -}; - -#ifdef COLOR_SCHEME_NODE_TYPE -static const int deg_debug_node_type_color_map[][2] = { - {DEPSNODE_TYPE_ROOT, 0}, - {DEPSNODE_TYPE_TIMESOURCE, 1}, - {DEPSNODE_TYPE_ID_REF, 2}, - {DEPSNODE_TYPE_SUBGRAPH, 3}, - - /* Outer Types */ - {DEPSNODE_TYPE_PARAMETERS, 4}, - {DEPSNODE_TYPE_PROXY, 5}, - {DEPSNODE_TYPE_ANIMATION, 6}, - {DEPSNODE_TYPE_TRANSFORM, 7}, - {DEPSNODE_TYPE_GEOMETRY, 8}, - {DEPSNODE_TYPE_SEQUENCER, 9}, - {DEPSNODE_TYPE_SHADING, 10}, - {-1, 0} -}; -#endif - -#if 0 /* unused */ -static const int deg_debug_relation_type_color_map[][2] = { - {DEPSREL_TYPE_STANDARD, 0}, - {DEPSREL_TYPE_ROOT_TO_ACTIVE, 1}, - {DEPSREL_TYPE_DATABLOCK, 2}, - {DEPSREL_TYPE_TIME, 3}, - {DEPSREL_TYPE_COMPONENT_ORDER, 4}, - {DEPSREL_TYPE_OPERATION, 5}, - {DEPSREL_TYPE_DRIVER, 6}, - {DEPSREL_TYPE_DRIVER_TARGET, 7}, - {DEPSREL_TYPE_TRANSFORM, 8}, - {DEPSREL_TYPE_GEOMETRY_EVAL, 9}, - {DEPSREL_TYPE_UPDATE, 10}, - {DEPSREL_TYPE_UPDATE_UI, 11}, - {-1, 0} -}; -#endif - -static int deg_debug_node_color_index(const DepsNode *node) -{ -#ifdef COLOR_SCHEME_NODE_CLASS - /* Some special types. */ - switch (node->type) { - case DEPSNODE_TYPE_ID_REF: - return 5; - case DEPSNODE_TYPE_OPERATION: - { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->is_noop()) - return 8; - break; - } - - default: - break; - } - /* Do others based on class. */ - switch (node->tclass) { - case DEPSNODE_CLASS_OPERATION: - return 4; - case DEPSNODE_CLASS_COMPONENT: - return 1; - default: - return 9; - } -#endif - -#ifdef COLOR_SCHEME_NODE_TYPE - const int (*pair)[2]; - for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { - if ((*pair)[0] == node->type) { - return (*pair)[1]; - } - } - return -1; -#endif -} - -struct DebugContext { - FILE *file; - bool show_tags; - bool show_eval_priority; -}; - -static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3); -static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(ctx.file, fmt, args); - va_end(args); -} - -static void deg_debug_graphviz_legend_color(const DebugContext &ctx, - const char *name, - const char *color) -{ - deg_debug_fprintf(ctx, "<TR>"); - deg_debug_fprintf(ctx, "<TD>%s</TD>", name); - deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color); - deg_debug_fprintf(ctx, "</TR>" NL); -} - -#if 0 -static void deg_debug_graphviz_legend_line(const DebugContext &ctx, - const char *name, - const char *color, - const char *style) -{ - /* XXX TODO */ - deg_debug_fprintf(ctx, "" NL); -} - -static void deg_debug_graphviz_legend_cluster(const DebugContext &ctx, - const char *name, - const char *color, - const char *style) -{ - deg_debug_fprintf(ctx, "<TR>"); - deg_debug_fprintf(ctx, "<TD>%s</TD>", name); - deg_debug_fprintf(ctx, "<TD CELLPADDING=\"4\"><TABLE BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">"); - deg_debug_fprintf(ctx, "<TR><TD BGCOLOR=\"%s\"></TD></TR>", color); - deg_debug_fprintf(ctx, "</TABLE></TD>"); - deg_debug_fprintf(ctx, "</TR>" NL); -} -#endif - -static void deg_debug_graphviz_legend(const DebugContext &ctx) -{ - deg_debug_fprintf(ctx, "{" NL); - deg_debug_fprintf(ctx, "rank = sink;" NL); - deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL); - deg_debug_fprintf(ctx, " <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL); - deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL); - -#ifdef COLOR_SCHEME_NODE_CLASS - const char **colors = deg_debug_colors_light; - deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]); - deg_debug_graphviz_legend_color(ctx, "Component", colors[1]); - deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]); - deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]); -#endif - -#ifdef COLOR_SCHEME_NODE_TYPE - const int (*pair)[2]; - for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { - DepsNodeFactory *nti = DEG_get_node_factory((eDepsNode_Type)(*pair)[0]); - deg_debug_graphviz_legend_color(ctx, - nti->tname().c_str(), - deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]); - } -#endif - - deg_debug_fprintf(ctx, "</TABLE>" NL); - deg_debug_fprintf(ctx, ">" NL); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, "}" NL); -} - -#if 0 /* unused */ -static int deg_debug_relation_type_color_index(eDepsRelation_Type type) -{ - const int (*pair)[2]; - for (pair = deg_debug_relation_type_color_map; (*pair)[0] >= 0; ++pair) { - if ((*pair)[0] == type) { - return (*pair)[1]; - } - } - return -1; -} -#endif - -static void deg_debug_graphviz_node_color(const DebugContext &ctx, - const DepsNode *node) -{ - const char *color_default = "black"; - const char *color_modified = "orangered4"; - const char *color_update = "dodgerblue3"; - const char *color = color_default; - if (ctx.show_tags) { - if (node->tclass == DEPSNODE_CLASS_OPERATION) { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { - color = color_modified; - } - else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { - color = color_update; - } - } - } - deg_debug_fprintf(ctx, "\"%s\"", color); -} - -static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx, - const DepsNode *node) -{ - float penwidth_default = 1.0f; - float penwidth_modified = 4.0f; - float penwidth_update = 4.0f; - float penwidth = penwidth_default; - if (ctx.show_tags) { - if (node->tclass == DEPSNODE_CLASS_OPERATION) { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { - penwidth = penwidth_modified; - } - else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { - penwidth = penwidth_update; - } - } - } - deg_debug_fprintf(ctx, "\"%f\"", penwidth); -} - -static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx, - const DepsNode *node) -{ - const char *defaultcolor = "gainsboro"; - int color_index = deg_debug_node_color_index(node); - const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors]; - deg_debug_fprintf(ctx, "\"%s\"", fillcolor); -} - -#if 0 /* implementation using stripes, a bit too noisy ... */ -static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx, - const DepsNode *node) -{ - const char *defaultcolor = "gainsboro"; - const char *color_needs_update = "orange"; - const int num_stripes = 10; - int color_index = deg_debug_node_color_index(node); - const char *base_color = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors]; - if (ctx.show_tags && - (node->flag & (DEPSNODE_FLAG_DIRECTLY_MODIFIED | DEPSNODE_FLAG_NEEDS_UPDATE))) - { - deg_debug_fprintf(ctx, "\""); - for (int i = 0; i < num_stripes; ++i) { - if (i > 0) { - deg_debug_fprintf(ctx, ":"); - } - deg_debug_fprintf(ctx, "%s:%s", base_color, color_needs_update); - } - deg_debug_fprintf(ctx, "\""); - } - else { - deg_debug_fprintf(ctx, "\"%s\"", base_color); - } -} -#endif - -static void deg_debug_graphviz_relation_color(const DebugContext &ctx, - const DepsRelation *rel) -{ - const char *color_default = "black"; - const char *color_error = "red4"; - const char *color = color_default; -#if 0 /* disabled for now, edge colors are hardly distinguishable */ - int color = deg_debug_relation_type_color_index(rel->type); - if (color < 0) { - deg_debug_fprintf(ctx, "%s", defaultcolor); - } - else { - deg_debug_fprintf(ctx, "\"%s\"", deg_debug_colors_dark[color % deg_debug_max_colors]); - } -#else - if (rel->flag & DEPSREL_FLAG_CYCLIC) - color = color_error; - - deg_debug_fprintf(ctx, "%s", color); -#endif -} - -static void deg_debug_graphviz_node_style(const DebugContext &ctx, const DepsNode *node) -{ - const char *base_style = "filled"; /* default style */ - if (ctx.show_tags) { - if (node->tclass == DEPSNODE_CLASS_OPERATION) { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) { - base_style = "striped"; - } - } - } - switch (node->tclass) { - case DEPSNODE_CLASS_GENERIC: - deg_debug_fprintf(ctx, "\"%s\"", base_style); - break; - case DEPSNODE_CLASS_COMPONENT: - deg_debug_fprintf(ctx, "\"%s\"", base_style); - break; - case DEPSNODE_CLASS_OPERATION: - deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style); - break; - } -} - -static void deg_debug_graphviz_node_single(const DebugContext &ctx, - const DepsNode *node) -{ - const char *shape = "box"; - string name = node->identifier(); - float priority = -1.0f; - if (node->type == DEPSNODE_TYPE_ID_REF) { - IDDepsNode *id_node = (IDDepsNode *)node; - char buf[256]; - BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); - name += buf; - } - if (ctx.show_eval_priority && node->tclass == DEPSNODE_CLASS_OPERATION) { - priority = ((OperationDepsNode *)node)->eval_priority; - } - deg_debug_fprintf(ctx, "// %s\n", name.c_str()); - deg_debug_fprintf(ctx, "\"node_%p\"", node); - deg_debug_fprintf(ctx, "["); -// deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name); - if (priority >= 0.0f) { - deg_debug_fprintf(ctx, "label=<%s<BR/>(<I>%.2f</I>)>", - name.c_str(), - priority); - } - else { - deg_debug_fprintf(ctx, "label=<%s>", name.c_str()); - } - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size); - deg_debug_fprintf(ctx, ",shape=%s", shape); - deg_debug_fprintf(ctx, ",style="); deg_debug_graphviz_node_style(ctx, node); - deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_node_color(ctx, node); - deg_debug_fprintf(ctx, ",fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); - deg_debug_fprintf(ctx, ",penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); -} - -static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx, - const DepsNode *node) -{ - string name = node->identifier().c_str(); - if (node->type == DEPSNODE_TYPE_ID_REF) { - IDDepsNode *id_node = (IDDepsNode *)node; - char buf[256]; - BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); - name += buf; - } - deg_debug_fprintf(ctx, "// %s\n", name.c_str()); - deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node); -// deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name); - deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str()); - deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size); - deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16); - deg_debug_fprintf(ctx, "style="); deg_debug_graphviz_node_style(ctx, node); deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "color="); deg_debug_graphviz_node_color(ctx, node); deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); deg_debug_fprintf(ctx, ";" NL); - /* dummy node, so we can add edges between clusters */ - deg_debug_fprintf(ctx, "\"node_%p\"", node); - deg_debug_fprintf(ctx, "["); - deg_debug_fprintf(ctx, "shape=%s", "point"); - deg_debug_fprintf(ctx, ",style=%s", "invis"); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); -} - -static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx) -{ - deg_debug_fprintf(ctx, "}" NL); - deg_debug_fprintf(ctx, NL); -} - -static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, - const Depsgraph *graph); -static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, - const Depsgraph *graph); - -static void deg_debug_graphviz_node(const DebugContext &ctx, - const DepsNode *node) -{ - switch (node->type) { - case DEPSNODE_TYPE_ID_REF: - { - const IDDepsNode *id_node = (const IDDepsNode *)node; - if (id_node->components.empty()) { - deg_debug_graphviz_node_single(ctx, node); - } - else { - deg_debug_graphviz_node_cluster_begin(ctx, node); - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) - { - const ComponentDepsNode *comp = it->second; - deg_debug_graphviz_node(ctx, comp); - } - deg_debug_graphviz_node_cluster_end(ctx); - } - break; - } - case DEPSNODE_TYPE_SUBGRAPH: - { - SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; - if (sub_node->graph) { - deg_debug_graphviz_node_cluster_begin(ctx, node); - deg_debug_graphviz_graph_nodes(ctx, sub_node->graph); - deg_debug_graphviz_node_cluster_end(ctx); - } - else { - deg_debug_graphviz_node_single(ctx, node); - } - break; - } - case DEPSNODE_TYPE_PARAMETERS: - case DEPSNODE_TYPE_ANIMATION: - case DEPSNODE_TYPE_TRANSFORM: - case DEPSNODE_TYPE_PROXY: - case DEPSNODE_TYPE_GEOMETRY: - case DEPSNODE_TYPE_SEQUENCER: - case DEPSNODE_TYPE_EVAL_POSE: - case DEPSNODE_TYPE_BONE: - case DEPSNODE_TYPE_SHADING: - case DEPSNODE_TYPE_EVAL_PARTICLES: - { - ComponentDepsNode *comp_node = (ComponentDepsNode *)node; - if (!comp_node->operations.empty()) { - deg_debug_graphviz_node_cluster_begin(ctx, node); - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - const DepsNode *op_node = it->second; - deg_debug_graphviz_node(ctx, op_node); - } - deg_debug_graphviz_node_cluster_end(ctx); - } - else { - deg_debug_graphviz_node_single(ctx, node); - } - break; - } - default: - deg_debug_graphviz_node_single(ctx, node); - break; - } -} - -static bool deg_debug_graphviz_is_cluster(const DepsNode *node) -{ - switch (node->type) { - case DEPSNODE_TYPE_ID_REF: - { - const IDDepsNode *id_node = (const IDDepsNode *)node; - return !id_node->components.empty(); - } - case DEPSNODE_TYPE_SUBGRAPH: - { - SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; - return sub_node->graph != NULL; - } - case DEPSNODE_TYPE_PARAMETERS: - case DEPSNODE_TYPE_ANIMATION: - case DEPSNODE_TYPE_TRANSFORM: - case DEPSNODE_TYPE_PROXY: - case DEPSNODE_TYPE_GEOMETRY: - case DEPSNODE_TYPE_SEQUENCER: - case DEPSNODE_TYPE_EVAL_POSE: - case DEPSNODE_TYPE_BONE: - { - ComponentDepsNode *comp_node = (ComponentDepsNode *)node; - return !comp_node->operations.empty(); - } - default: - return false; - } -} - -static bool deg_debug_graphviz_is_owner(const DepsNode *node, - const DepsNode *other) -{ - switch (node->tclass) { - case DEPSNODE_CLASS_COMPONENT: - { - ComponentDepsNode *comp_node = (ComponentDepsNode *)node; - if (comp_node->owner == other) - return true; - break; - } - case DEPSNODE_CLASS_OPERATION: - { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->owner == other) - return true; - else if (op_node->owner->owner == other) - return true; - break; - } - default: break; - } - return false; -} - -static void deg_debug_graphviz_node_relations(const DebugContext &ctx, - const DepsNode *node) -{ - DEPSNODE_RELATIONS_ITER_BEGIN(node->inlinks, rel) - { - float penwidth = 2.0f; - - const DepsNode *tail = rel->to; /* same as node */ - const DepsNode *head = rel->from; - deg_debug_fprintf(ctx, "// %s -> %s\n", - head->identifier().c_str(), - tail->identifier().c_str()); - deg_debug_fprintf(ctx, "\"node_%p\"", head); - deg_debug_fprintf(ctx, " -> "); - deg_debug_fprintf(ctx, "\"node_%p\"", tail); - - deg_debug_fprintf(ctx, "["); - /* XXX labels on relations are not very helpful: - * - they tend to appear too far away to be associated with the edge lines - * - names are mostly redundant, reflecting simply their from/to nodes - * - no behavior or typing of relations themselves to justify labels - */ -#if 0 - deg_debug_fprintf(ctx, "label=\"%s\"", rel->name); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); -#else - /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */ - deg_debug_fprintf(ctx, "id=\"%s\"", rel->name); -#endif - deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_relation_color(ctx, rel); - deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth); - /* NOTE: edge from node to own cluster is not possible and gives graphviz - * warning, avoid this here by just linking directly to the invisible - * placeholder node - */ - if (deg_debug_graphviz_is_cluster(tail) && !deg_debug_graphviz_is_owner(head, tail)) { - deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail); - } - if (deg_debug_graphviz_is_cluster(head) && !deg_debug_graphviz_is_owner(tail, head)) { - deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head); - } - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); - } - DEPSNODE_RELATIONS_ITER_END; - -#if 0 - if (node->tclass == DEPSNODE_CLASS_COMPONENT) { - const ComponentDepsNode *comp_node = (const ComponentDepsNode *)node; - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - OperationDepsNode *op_node = it->second; - deg_debug_graphviz_node_relations(ctx, op_node); - } - } - else if (node->type == DEPSNODE_TYPE_ID_REF) { - const IDDepsNode *id_node = (const IDDepsNode *)node; - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) - { - const ComponentDepsNode *comp = it->second; - deg_debug_graphviz_node_relations(ctx, comp); - } - } - else if (node->type == DEPSNODE_TYPE_SUBGRAPH) { - SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; - if (sub_node->graph) { - deg_debug_graphviz_graph_relations(ctx, sub_node->graph); - } - } -#endif -} - -static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, - const Depsgraph *graph) -{ - if (graph->root_node) { - deg_debug_graphviz_node(ctx, graph->root_node); - } - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) - { - DepsNode *node = it->second; - deg_debug_graphviz_node(ctx, node); - } - TimeSourceDepsNode *time_source = graph->find_time_source(NULL); - if (time_source != NULL) { - deg_debug_graphviz_node(ctx, time_source); - } -} - -static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, - const Depsgraph *graph) -{ -#if 0 - if (graph->root_node) { - deg_debug_graphviz_node_relations(ctx, graph->root_node); - } - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) - { - DepsNode *id_node = it->second; - deg_debug_graphviz_node_relations(ctx, id_node); - } -#else - /* XXX not in use yet */ -// for (Depsgraph::OperationNodes::const_iterator it = graph->all_opnodes.begin(); -// it != graph->all_opnodes.end(); -// ++it) -// { -// OperationDepsNode *op_node = *it; -// deg_debug_graphviz_node_relations(ctx, op_node); -// } - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) - { - IDDepsNode *id_node = it->second; - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) - { - ComponentDepsNode *comp_node = it->second; - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - OperationDepsNode *op_node = it->second; - deg_debug_graphviz_node_relations(ctx, op_node); - } - } - } - - TimeSourceDepsNode *time_source = graph->find_time_source(NULL); - if (time_source != NULL) { - deg_debug_graphviz_node_relations(ctx, time_source); - } -#endif -} - -void DEG_debug_graphviz(const Depsgraph *graph, FILE *f, const char *label, bool show_eval) -{ -#if 0 /* generate shaded color set */ - static char colors[][3] = {{0xa6, 0xce, 0xe3},{0x1f, 0x78, 0xb4},{0xb2, 0xdf, 0x8a},{0x33, 0xa0, 0x2c}, - {0xfb, 0x9a, 0x99},{0xe3, 0x1a, 0x1c},{0xfd, 0xbf, 0x6f},{0xff, 0x7f, 0x00}, - {0xca, 0xb2, 0xd6},{0x6a, 0x3d, 0x9a},{0xff, 0xff, 0x99},{0xb1, 0x59, 0x28}}; - int i; - const float factor = 0.666f; - for (i=0; i < 12; ++i) - printf("\"#%x%x%x\"\n", (char)(colors[i][0] * factor), (char)(colors[i][1] * factor), (char)(colors[i][2] * factor)); -#endif - - if (!graph) { - return; - } - - DebugContext ctx; - ctx.file = f; - ctx.show_tags = show_eval; - ctx.show_eval_priority = show_eval; - - deg_debug_fprintf(ctx, "digraph depgraph {" NL); - deg_debug_fprintf(ctx, "rankdir=LR;" NL); - deg_debug_fprintf(ctx, "graph ["); - deg_debug_fprintf(ctx, "compound=true"); - deg_debug_fprintf(ctx, ",labelloc=\"t\""); - deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_graph_label_size); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, ",label=\"%s\"", label); - deg_debug_fprintf(ctx, ",splines=ortho"); - deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato - deg_debug_fprintf(ctx, "];" NL); - - deg_debug_graphviz_graph_nodes(ctx, graph); - deg_debug_graphviz_graph_relations(ctx, graph); - - deg_debug_graphviz_legend(ctx); - - deg_debug_fprintf(ctx, "}" NL); -} - -#undef NL +#include "intern/eval/deg_eval_debug.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" /* ************************************************ */ -static string get_component_name(eDepsNode_Type type, const string &name = "") -{ - DepsNodeFactory *factory = DEG_get_node_factory(type); - if (name.empty()) { - return string(factory->tname()); - } - else { - return string(factory->tname()) + " | " + name; - } -} - -static void times_clear(DepsgraphStatsTimes ×) -{ - times.duration_last = 0.0f; -} - -static void times_add(DepsgraphStatsTimes ×, float time) -{ - times.duration_last += time; -} - -void DepsgraphDebug::eval_begin(const EvaluationContext *UNUSED(eval_ctx)) -{ - /* TODO(sergey): Stats are currently globally disabled. */ - /* verify_stats(); */ - reset_stats(); -} - -void DepsgraphDebug::eval_end(const EvaluationContext *UNUSED(eval_ctx)) -{ - WM_main_add_notifier(NC_SPACE | ND_SPACE_INFO_REPORT, NULL); -} - -void DepsgraphDebug::eval_step(const EvaluationContext *UNUSED(eval_ctx), - const char *message) -{ -#ifdef DEG_DEBUG_BUILD - if (deg_debug_eval_cb) - deg_debug_eval_cb(deg_debug_eval_userdata, message); -#else - (void)message; /* Ignored. */ -#endif -} - -void DepsgraphDebug::task_started(Depsgraph *graph, - const OperationDepsNode *node) -{ - if (stats) { - BLI_spin_lock(&graph->lock); - - ComponentDepsNode *comp = node->owner; - ID *id = comp->owner->id; - - DepsgraphStatsID *id_stats = get_id_stats(id, true); - times_clear(id_stats->times); - - /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ - if (0) { - /* XXX component name usage needs cleanup! currently mixes identifier and description strings! */ - DepsgraphStatsComponent *comp_stats = get_component_stats(id, get_component_name(comp->type, comp->name), true); - times_clear(comp_stats->times); - } - - BLI_spin_unlock(&graph->lock); - } -} - -void DepsgraphDebug::task_completed(Depsgraph *graph, - const OperationDepsNode *node, - double time) -{ - if (stats) { - BLI_spin_lock(&graph->lock); - - ComponentDepsNode *comp = node->owner; - ID *id = comp->owner->id; - - DepsgraphStatsID *id_stats = get_id_stats(id, true); - times_add(id_stats->times, time); - - /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ - if (0) { - /* XXX component name usage needs cleanup! currently mixes identifier and description strings! */ - DepsgraphStatsComponent *comp_stats = get_component_stats(id, get_component_name(comp->type, comp->name), true); - times_add(comp_stats->times, time); - } - - BLI_spin_unlock(&graph->lock); - } -} - -/* ********** */ -/* Statistics */ - -DepsgraphStats *DepsgraphDebug::stats = NULL; - -/* GHash callback */ -static void deg_id_stats_free(void *val) -{ - DepsgraphStatsID *id_stats = (DepsgraphStatsID *)val; - - if (id_stats) { - BLI_freelistN(&id_stats->components); - MEM_freeN(id_stats); - } -} - -void DepsgraphDebug::stats_init() -{ - if (!stats) { - stats = (DepsgraphStats *)MEM_callocN(sizeof(DepsgraphStats), "Depsgraph Stats"); - stats->id_stats = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "Depsgraph ID Stats Hash"); - } -} - -void DepsgraphDebug::stats_free() -{ - if (stats) { - BLI_ghash_free(stats->id_stats, NULL, deg_id_stats_free); - MEM_freeN(stats); - stats = NULL; - } -} - -void DepsgraphDebug::verify_stats() -{ - stats_init(); -} - -void DepsgraphDebug::reset_stats() -{ - if (!stats) { - return; - } - - /* XXX this doesn't work, will immediately clear all info, - * since most depsgraph updates have none or very few updates to handle. - * - * Could consider clearing only zero-user ID blocks here - */ -// BLI_ghash_clear(stats->id_stats, NULL, deg_id_stats_free); -} - -DepsgraphStatsID *DepsgraphDebug::get_id_stats(ID *id, bool create) -{ - DepsgraphStatsID *id_stats = (DepsgraphStatsID *)BLI_ghash_lookup(stats->id_stats, id); - - if (!id_stats && create) { - id_stats = (DepsgraphStatsID *)MEM_callocN(sizeof(DepsgraphStatsID), "Depsgraph ID Stats"); - id_stats->id = id; - - BLI_ghash_insert(stats->id_stats, id, id_stats); - } - - return id_stats; -} - -DepsgraphStatsComponent *DepsgraphDebug::get_component_stats( - DepsgraphStatsID *id_stats, - const string &name, - bool create) -{ - DepsgraphStatsComponent *comp_stats; - for (comp_stats = (DepsgraphStatsComponent *)id_stats->components.first; - comp_stats != NULL; - comp_stats = comp_stats->next) - { - if (STREQ(comp_stats->name, name.c_str())) - break; - } - if (!comp_stats && create) { - comp_stats = (DepsgraphStatsComponent *)MEM_callocN(sizeof(DepsgraphStatsComponent), "Depsgraph Component Stats"); - BLI_strncpy(comp_stats->name, name.c_str(), sizeof(comp_stats->name)); - BLI_addtail(&id_stats->components, comp_stats); - } - return comp_stats; -} - -/* ------------------------------------------------ */ - DepsgraphStats *DEG_stats(void) { - return DepsgraphDebug::stats; + return DEG::DepsgraphDebug::stats; } void DEG_stats_verify() { - DepsgraphDebug::verify_stats(); + DEG::DepsgraphDebug::verify_stats(); } DepsgraphStatsID *DEG_stats_id(ID *id) { - if (!DepsgraphDebug::stats) { + if (!DEG::DepsgraphDebug::stats) { return NULL; } - return DepsgraphDebug::get_id_stats(id, false); + return DEG::DepsgraphDebug::get_id_stats(id, false); } bool DEG_debug_compare(const struct Depsgraph *graph1, @@ -987,7 +70,9 @@ bool DEG_debug_compare(const struct Depsgraph *graph1, { BLI_assert(graph1 != NULL); BLI_assert(graph2 != NULL); - if (graph1->operations.size() != graph2->operations.size()) { + const DEG::Depsgraph *deg_graph1 = reinterpret_cast<const DEG::Depsgraph *>(graph1); + const DEG::Depsgraph *deg_graph2 = reinterpret_cast<const DEG::Depsgraph *>(graph2); + if (deg_graph1->operations.size() != deg_graph2->operations.size()) { return false; } /* TODO(sergey): Currently we only do real stupid check, @@ -1017,34 +102,21 @@ bool DEG_debug_scene_relations_validate(Main *bmain, bool DEG_debug_consistency_check(Depsgraph *graph) { - /* Validate links exists in both directions. */ - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + /* Validate links exists in both directions. */ + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { + foreach (DEG::DepsRelation *rel, node->outlinks) { int counter1 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = node->outlinks.begin(); - tmp_rel != node->outlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, node->outlinks) { + if (tmp_rel == rel) { ++counter1; } } int counter2 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = rel->to->inlinks.begin(); - tmp_rel != rel->to->inlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, rel->to->inlinks) { + if (tmp_rel == rel) { ++counter2; } } @@ -1057,33 +129,18 @@ bool DEG_debug_consistency_check(Depsgraph *graph) } } - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { + foreach (DEG::DepsRelation *rel, node->inlinks) { int counter1 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = node->inlinks.begin(); - tmp_rel != node->inlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, node->inlinks) { + if (tmp_rel == rel) { ++counter1; } } int counter2 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = rel->from->outlinks.begin(); - tmp_rel != rel->from->outlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, rel->from->outlinks) { + if (tmp_rel == rel) { ++counter2; } } @@ -1096,32 +153,20 @@ bool DEG_debug_consistency_check(Depsgraph *graph) } /* Validate node valency calculated in both directions. */ - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { node->num_links_pending = 0; node->done = 0; } - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { if (node->done) { printf("Node %s is twice in the operations!\n", node->identifier().c_str()); return false; } - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; + foreach (DEG::DepsRelation *rel, node->outlinks) { + if (rel->to->type == DEG::DEPSNODE_TYPE_OPERATION) { + DEG::OperationDepsNode *to = (DEG::OperationDepsNode *)rel->to; BLI_assert(to->num_links_pending < to->inlinks.size()); ++to->num_links_pending; } @@ -1129,18 +174,10 @@ bool DEG_debug_consistency_check(Depsgraph *graph) node->done = 1; } - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { int num_links_pending = 0; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + foreach (DEG::DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEG::DEPSNODE_TYPE_OPERATION) { ++num_links_pending; } } @@ -1166,12 +203,14 @@ bool DEG_debug_consistency_check(Depsgraph *graph) void DEG_stats_simple(const Depsgraph *graph, size_t *r_outer, size_t *r_operations, size_t *r_relations) { + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + /* number of operations */ if (r_operations) { /* All operations should be in this list, allowing us to count the total * number of nodes. */ - *r_operations = graph->operations.size(); + *r_operations = deg_graph->operations.size(); } /* Count number of outer nodes and/or relations between these. */ @@ -1179,29 +218,21 @@ void DEG_stats_simple(const Depsgraph *graph, size_t *r_outer, size_t tot_outer = 0; size_t tot_rels = 0; - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) + GHASH_FOREACH_BEGIN(DEG::IDDepsNode *, id_node, deg_graph->id_hash) { - IDDepsNode *id_node = it->second; tot_outer++; - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) + GHASH_FOREACH_BEGIN(DEG::ComponentDepsNode *, comp_node, id_node->components) { - ComponentDepsNode *comp_node = it->second; tot_outer++; - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - OperationDepsNode *op_node = it->second; + foreach (DEG::OperationDepsNode *op_node, comp_node->operations) { tot_rels += op_node->inlinks.size(); } } + GHASH_FOREACH_END(); } + GHASH_FOREACH_END(); - TimeSourceDepsNode *time_source = graph->find_time_source(NULL); + DEG::TimeSourceDepsNode *time_source = deg_graph->find_time_source(NULL); if (time_source != NULL) { tot_rels += time_source->inlinks.size(); } @@ -1210,4 +241,3 @@ void DEG_stats_simple(const Depsgraph *graph, size_t *r_outer, if (r_outer) *r_outer = tot_outer; } } - diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 66535f5214b..f8d40d0e6a8 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -32,11 +32,9 @@ #include "MEM_guardedalloc.h" -#include "PIL_time.h" - extern "C" { #include "BLI_utildefines.h" -#include "BLI_task.h" +#include "BLI_ghash.h" #include "BKE_depsgraph.h" #include "BKE_scene.h" @@ -44,13 +42,13 @@ extern "C" { #include "DEG_depsgraph.h" } /* extern "C" */ -#include "atomic_ops.h" +#include "intern/eval/deg_eval.h" +#include "intern/eval/deg_eval_flush.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_operation.h" -#include "depsgraph.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_debug.h" +#include "intern/depsgraph.h" #ifdef WITH_LEGACY_DEPSGRAPH static bool use_legacy_depsgraph = true; @@ -118,359 +116,16 @@ void DEG_evaluation_context_free(EvaluationContext *eval_ctx) MEM_freeN(eval_ctx); } -/* ********************** */ -/* Evaluation Entrypoints */ - -/* Forward declarations. */ -static void schedule_children(TaskPool *pool, - Depsgraph *graph, - OperationDepsNode *node, - const int layers, - const int thread_id); - -struct DepsgraphEvalState { - EvaluationContext *eval_ctx; - Depsgraph *graph; - int layers; -}; - -static void deg_task_run_func(TaskPool *pool, - void *taskdata, - int thread_id) -{ - DepsgraphEvalState *state = (DepsgraphEvalState *)BLI_task_pool_userdata(pool); - OperationDepsNode *node = (OperationDepsNode *)taskdata; - - BLI_assert(!node->is_noop() && "NOOP nodes should not actually be scheduled"); - - /* Should only be the case for NOOPs, which never get to this point. */ - BLI_assert(node->evaluate); - - while (true) { - /* Get context. */ - // TODO: who initialises this? "Init" operations aren't able to initialise it!!! - /* TODO(sergey): We don't use component contexts at this moment. */ - /* ComponentDepsNode *comp = node->owner; */ - BLI_assert(node->owner != NULL); - - /* Since we're not leaving the thread for until the graph branches it is - * possible to have NO-OP on the way. for which evaluate() will be NULL. - * but that's all fine, we'll just scheduler it's children. - */ - if (node->evaluate) { - /* Take note of current time. */ - double start_time = PIL_check_seconds_timer(); - DepsgraphDebug::task_started(state->graph, node); - - /* Perform operation. */ - node->evaluate(state->eval_ctx); - - /* Note how long this took. */ - double end_time = PIL_check_seconds_timer(); - DepsgraphDebug::task_completed(state->graph, - node, - end_time - start_time); - } - - /* If there's only one outgoing link we try to immediately switch to - * that node evaluation, without leaving the thread. - * - * It's only doable if the child don't have extra relations or all they - * are satisfied. - * - * TODO(sergey): Checks here can be de-duplicated with the ones from - * schedule_node(), however, how to do it nicely? - */ - if (node->outlinks.size() == 1) { - DepsRelation *rel = node->outlinks[0]; - OperationDepsNode *child = (OperationDepsNode *)rel->to; - BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); - if (!child->scheduled) { - int id_layers = child->owner->owner->layers; - if (!((child->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 && - (id_layers & state->layers) != 0)) - { - /* Node does not need an update, so can;t continue with the - * chain and need to switch to another one by leaving the - * thread. - */ - break; - } - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - BLI_assert(child->num_links_pending > 0); - atomic_sub_uint32(&child->num_links_pending, 1); - } - if (child->num_links_pending == 0) { - bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&child->scheduled, (uint8_t)true); - if (!is_scheduled) { - /* Node was not scheduled, switch to it! */ - node = child; - } - else { - /* Someone else scheduled the node, leaving us - * unemployed in this thread, we're leaving. - */ - break; - } - } - else { - /* There are other dependencies on the child, can't do - * anything in the current thread. - */ - break; - } - } - else { - /* Happens when having cyclic dependencies. - * - * Nothing to do here, single child was already scheduled, we - * can leave the thread now. - */ - break; - } - } - else { - /* TODO(sergey): It's possible to use one of the outgoing relations - * as a chain which we'll try to keep alive, but it's a bit more - * involved change. - */ - schedule_children(pool, state->graph, node, state->layers, thread_id); - break; - } - } -} - -typedef struct CalculatePengindData { - Depsgraph *graph; - int layers; -} CalculatePengindData; - -static void calculate_pending_func(void *data_v, int i) -{ - CalculatePengindData *data = (CalculatePengindData *)data_v; - Depsgraph *graph = data->graph; - int layers = data->layers; - OperationDepsNode *node = graph->operations[i]; - IDDepsNode *id_node = node->owner->owner; - - node->num_links_pending = 0; - node->scheduled = false; - - /* count number of inputs that need updates */ - if ((id_node->layers & layers) != 0 && - (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) - { - DEPSNODE_RELATIONS_ITER_BEGIN(node->inlinks, rel) - { - if (rel->from->type == DEPSNODE_TYPE_OPERATION && - (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) - { - OperationDepsNode *from = (OperationDepsNode *)rel->from; - IDDepsNode *id_from_node = from->owner->owner; - if ((id_from_node->layers & layers) != 0 && - (from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) - { - ++node->num_links_pending; - } - } - } - DEPSNODE_RELATIONS_ITER_END; - } -} - -static void calculate_pending_parents(Depsgraph *graph, int layers) -{ - const int num_operations = graph->operations.size(); - const bool do_threads = num_operations > 256; - CalculatePengindData data; - data.graph = graph; - data.layers = layers; - BLI_task_parallel_range(0, num_operations, &data, calculate_pending_func, do_threads); -} - -#ifdef USE_EVAL_PRIORITY -static void calculate_eval_priority(OperationDepsNode *node) -{ - if (node->done) { - return; - } - node->done = 1; - - if (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { - /* XXX standard cost of a node, could be estimated somewhat later on */ - const float cost = 1.0f; - /* NOOP nodes have no cost */ - node->eval_priority = node->is_noop() ? cost : 0.0f; - - for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); - it != node->outlinks.end(); - ++it) - { - DepsRelation *rel = *it; - OperationDepsNode *to = (OperationDepsNode *)rel->to; - BLI_assert(to->type == DEPSNODE_TYPE_OPERATION); - calculate_eval_priority(to); - node->eval_priority += to->eval_priority; - } - } - else { - node->eval_priority = 0.0f; - } -} -#endif - -/* Schedule a node if it needs evaluation. - * dec_parents: Decrement pending parents count, true when child nodes are scheduled - * after a task has been completed. - */ -static void schedule_node(TaskPool *pool, Depsgraph *graph, int layers, - OperationDepsNode *node, bool dec_parents, - const int thread_id) -{ - int id_layers = node->owner->owner->layers; - - if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 && - (id_layers & layers) != 0) - { - if (dec_parents) { - BLI_assert(node->num_links_pending > 0); - atomic_sub_uint32(&node->num_links_pending, 1); - } - - if (node->num_links_pending == 0) { - bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&node->scheduled, (uint8_t)true); - if (!is_scheduled) { - if (node->is_noop()) { - /* skip NOOP node, schedule children right away */ - schedule_children(pool, graph, node, layers, thread_id); - } - else { - /* children are scheduled once this task is completed */ - BLI_task_pool_push_from_thread(pool, - deg_task_run_func, - node, - false, - TASK_PRIORITY_LOW, - thread_id); - } - } - } - } -} - -static void schedule_graph(TaskPool *pool, - Depsgraph *graph, - const int layers) -{ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; - schedule_node(pool, graph, layers, node, false, 0); - } -} - -static void schedule_children(TaskPool *pool, - Depsgraph *graph, - OperationDepsNode *node, - const int layers, - const int thread_id) -{ - DEPSNODE_RELATIONS_ITER_BEGIN(node->outlinks, rel) - { - OperationDepsNode *child = (OperationDepsNode *)rel->to; - BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); - if (child->scheduled) { - /* Happens when having cyclic dependencies. */ - continue; - } - schedule_node(pool, graph, layers, child, (rel->flag & DEPSREL_FLAG_CYCLIC) == 0, thread_id); - } - DEPSNODE_RELATIONS_ITER_END; -} - -/** - * Evaluate all nodes tagged for updating, - * \warning This is usually done as part of main loop, but may also be - * called from frame-change update. - * - * \note Time sources should be all valid! - */ -void DEG_evaluate_on_refresh_ex(EvaluationContext *eval_ctx, - Depsgraph *graph, - const int layers) -{ - /* Generate base evaluation context, upon which all the others are derived. */ - // TODO: this needs both main and scene access... - - /* Nothing to update, early out. */ - if (graph->entry_tags.size() == 0) { - return; - } - - /* Set time for the current graph evaluation context. */ - TimeSourceDepsNode *time_src = graph->find_time_source(); - eval_ctx->ctime = time_src->cfra; - - /* XXX could use a separate pool for each eval context */ - DepsgraphEvalState state; - state.eval_ctx = eval_ctx; - state.graph = graph; - state.layers = layers; - - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &state); - - if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { - BLI_pool_set_num_threads(task_pool, 1); - } - - calculate_pending_parents(graph, layers); - - /* Clear tags. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; - node->done = 0; - } - - /* Calculate priority for operation nodes. */ -#ifdef USE_EVAL_PRIORITY - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; - calculate_eval_priority(node); - } -#endif - - DepsgraphDebug::eval_begin(eval_ctx); - - schedule_graph(task_pool, graph, layers); - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - DepsgraphDebug::eval_end(eval_ctx); - - /* Clear any uncleared tags - just in case. */ - DEG_graph_clear_tags(graph); -} - /* Evaluate all nodes tagged for updating. */ void DEG_evaluate_on_refresh(EvaluationContext *eval_ctx, Depsgraph *graph, Scene *scene) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); /* Update time on primary timesource. */ - TimeSourceDepsNode *tsrc = graph->find_time_source(); + DEG::TimeSourceDepsNode *tsrc = deg_graph->find_time_source(); tsrc->cfra = BKE_scene_frame_get(scene); - - DEG_evaluate_on_refresh_ex(eval_ctx, graph, graph->layers); + DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph, deg_graph->layers); } /* Frame-change happened for root scene that graph belongs to. */ @@ -480,19 +135,18 @@ void DEG_evaluate_on_framechange(EvaluationContext *eval_ctx, float ctime, const int layers) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); /* Update time on primary timesource. */ - TimeSourceDepsNode *tsrc = graph->find_time_source(); + DEG::TimeSourceDepsNode *tsrc = deg_graph->find_time_source(); tsrc->cfra = ctime; - - tsrc->tag_update(graph); - - DEG_graph_flush_updates(bmain, graph); - + tsrc->tag_update(deg_graph); + DEG::deg_graph_flush_updates(bmain, deg_graph); /* Perform recalculation updates. */ - DEG_evaluate_on_refresh_ex(eval_ctx, graph, layers); + DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph, layers); } bool DEG_needs_eval(Depsgraph *graph) { - return graph->entry_tags.size() != 0; + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + return BLI_gset_size(deg_graph->entry_tags) != 0; } diff --git a/source/blender/depsgraph/intern/depsgraph_intern.h b/source/blender/depsgraph/intern/depsgraph_intern.h index 7fdc2454564..e5d3d1f5861 100644 --- a/source/blender/depsgraph/intern/depsgraph_intern.h +++ b/source/blender/depsgraph/intern/depsgraph_intern.h @@ -31,65 +31,26 @@ * - Also, defines for "Node Type Info" */ -#ifndef __DEPSGRAPH_INTERN_H__ -#define __DEPSGRAPH_INTERN_H__ +#pragma once #include <cstdlib> #include "MEM_guardedalloc.h" -#include "depsgraph.h" -#include "depsnode.h" +extern "C" { +#include "BKE_global.h" +} + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph.h" struct Main; struct Group; struct Scene; -/* Graph Building ======================================================== */ - -/** - * Build depsgraph for the given group, and dump results in given graph container - * This is usually used for building subgraphs for groups to use... - */ -void DEG_graph_build_from_group(Depsgraph *graph, struct Main *bmain, struct Group *group); - -/* Build subgraph for group */ -DepsNode *DEG_graph_build_group_subgraph(Depsgraph *graph_main, struct Main *bmain, struct Group *group); - -/* Graph Copying ========================================================= */ -/* (Part of the Filtering API) */ - -/** - * Depsgraph Copying Context (dcc) - * - * Keeps track of node relationships/links/etc. during the copy - * operation so that they can be safely remapped... - */ -typedef struct DepsgraphCopyContext { - struct GHash *nodes_hash; /* <DepsNode, DepsNode> mapping from src node to dst node */ - struct GHash *rels_hash; // XXX: same for relationships? - - // XXX: filtering criteria... -} DepsgraphCopyContext; - -/* Internal Filtering API ---------------------------------------------- */ - -/* Create filtering context */ -// XXX: needs params for conditions? -DepsgraphCopyContext *DEG_filter_init(void); - -/* Free filtering context once filtering is done */ -void DEG_filter_cleanup(DepsgraphCopyContext *dcc); - - -/* Data Copy Operations ------------------------------------------------ */ - -/** - * Make a (deep) copy of provided node and it's little subgraph - * \warning Newly created node is not added to the existing graph - * \param dcc: Context info for helping resolve links - */ -DepsNode *DEG_copy_node(DepsgraphCopyContext *dcc, const DepsNode *src); +namespace DEG { /* Node Types Handling ================================================= */ @@ -101,8 +62,9 @@ struct DepsNodeFactory { virtual eDepsNode_Class tclass() const = 0; virtual const char *tname() const = 0; - virtual DepsNode *create_node(const ID *id, const string &subdata, const string &name) const = 0; - virtual DepsNode *copy_node(DepsgraphCopyContext *dcc, const DepsNode *copy) const = 0; + virtual DepsNode *create_node(const ID *id, + const string &subdata, + const string &name) const = 0; }; template <class NodeType> @@ -130,34 +92,18 @@ struct DepsNodeFactoryImpl : public DepsNodeFactory { return node; } - - virtual DepsNode *copy_node(DepsgraphCopyContext *dcc, const DepsNode *copy) const - { - BLI_assert(copy->type == type()); - DepsNode *node = OBJECT_GUARDED_NEW(NodeType); - - /* populate base node settings */ - node->type = type(); - node->tclass = tclass(); - // XXX: need to review the name here, as we can't have exact duplicates... - node->name = copy->name; - - node->copy(dcc, static_cast<const NodeType *>(copy)); - - return node; - } }; /* Typeinfo Management -------------------------------------------------- */ /* Register typeinfo */ -void DEG_register_node_typeinfo(DepsNodeFactory *factory); +void deg_register_node_typeinfo(DepsNodeFactory *factory); /* Get typeinfo for specified type */ -DepsNodeFactory *DEG_get_node_factory(const eDepsNode_Type type); +DepsNodeFactory *deg_get_node_factory(const eDepsNode_Type type); /* Get typeinfo for provided node */ -DepsNodeFactory *DEG_node_get_factory(const DepsNode *node); +DepsNodeFactory *deg_node_get_factory(const DepsNode *node); /* Editors Integration -------------------------------------------------- */ @@ -165,4 +111,11 @@ void deg_editors_id_update(struct Main *bmain, struct ID *id); void deg_editors_scene_update(struct Main *bmain, struct Scene *scene, bool updated); -#endif /* __DEPSGRAPH_INTERN_H__ */ +#define DEG_DEBUG_PRINTF(...) \ + do { \ + if (G.debug & G_DEBUG_DEPSGRAPH) { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + } while (0) + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 73193747b93..cac4eaae215 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -33,162 +33,12 @@ #include "MEM_guardedalloc.h" extern "C" { -#include "BLI_utildefines.h" -#include "BLI_ghash.h" - #include "BKE_main.h" #include "DEG_depsgraph_query.h" } /* extern "C" */ -#include "depsgraph_queue.h" -#include "depsnode.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" - -/* ************************* */ -/* Low-Level Graph Traversal */ - -#if 0 -/* Prepare for graph traversal, by tagging nodes, etc. */ -static void DEG_graph_traverse_begin(Depsgraph * /*graph*/) -{ - /* go over all nodes, initialising the valence counts */ - // XXX: this will end up being O(|V|), which is bad when we're just updating a few nodes... -} - -/* Perform a traversal of graph from given starting node (in execution order) */ -// TODO: additional flags for controlling the process? -void DEG_graph_traverse_from_node(Depsgraph *graph, OperationDepsNode *start_node, - DEG_FilterPredicate filter, void *filter_data, - DEG_NodeOperation op, void *operation_data) -{ - DepsgraphQueue *q; - - /* sanity checks */ - if (ELEM(NULL, graph, start_node, op)) - return; - - /* add node as starting node to be evaluated, with value of 0 */ - q = DEG_queue_new(); - - start_node->num_links_pending = 0; - DEG_queue_push(q, start_node, 0.0f); - - /* while we still have nodes in the queue, grab and work on next one */ - do { - /* grab item at front of queue */ - // XXX: in practice, we may need to wait until one becomes available... - OperationDepsNode *node = (OperationDepsNode *)DEG_queue_pop(q); - - /* perform operation on node */ - op(graph, node, operation_data); - - /* schedule up operations which depend on this */ - DEPSNODE_RELATIONS_ITER_BEGIN(node->outlinks, rel) - { - /* ensure that relationship is not tagged for ignoring (i.e. cyclic, etc.) */ - // TODO: cyclic refs should probably all get clustered towards the end, so that we can just stop on the first one - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - OperationDepsNode *child_node = (OperationDepsNode *)rel->to; - - /* only visit node if the filtering function agrees */ - if ((filter == NULL) || filter(graph, child_node, filter_data)) { - /* schedule up node... */ - child_node->num_links_pending--; - DEG_queue_push(q, child_node, (float)child_node->num_links_pending); - } - } - } - DEPSNODE_RELATIONS_ITER_END; - } while (DEG_queue_is_empty(q) == false); - - /* cleanup */ - DEG_queue_free(q); -} -#endif - -/* ************************************************************** */ -/* Filtering API - Basically, making a copy of the existing graph */ - -/* Create filtering context */ -// TODO: allow passing in a number of criteria? -DepsgraphCopyContext *DEG_filter_init() -{ - DepsgraphCopyContext *dcc = (DepsgraphCopyContext *)MEM_callocN(sizeof(DepsgraphCopyContext), "DepsgraphCopyContext"); - - /* init hashes for easy lookups */ - dcc->nodes_hash = BLI_ghash_ptr_new("Depsgraph Filter NodeHash"); - dcc->rels_hash = BLI_ghash_ptr_new("Depsgraph Filter Relationship Hash"); // XXX? - - /* store filtering criteria? */ - // xxx... - - return dcc; -} - -/* Cleanup filtering context */ -void DEG_filter_cleanup(DepsgraphCopyContext *dcc) -{ - /* sanity check */ - if (dcc == NULL) - return; - - /* free hashes - contents are weren't copied, so are ok... */ - BLI_ghash_free(dcc->nodes_hash, NULL, NULL); - BLI_ghash_free(dcc->rels_hash, NULL, NULL); - - /* clear filtering criteria */ - // ... - - /* free dcc itself */ - MEM_freeN(dcc); -} - -/* -------------------------------------------------- */ - -/* Create a copy of provided node */ -// FIXME: the handling of sub-nodes and links will need to be subject to filtering options... -// XXX: perhaps this really shouldn't be exposed, as it will just be a sub-step of the evaluation process? -DepsNode *DEG_copy_node(DepsgraphCopyContext *dcc, const DepsNode *src) -{ - /* sanity check */ - if (src == NULL) - return NULL; - - DepsNodeFactory *factory = DEG_get_node_factory(src->type); - BLI_assert(factory != NULL); - DepsNode *dst = factory->copy_node(dcc, src); - - /* add this node-pair to the hash... */ - BLI_ghash_insert(dcc->nodes_hash, (DepsNode *)src, dst); - -#if 0 /* XXX TODO */ - /* now, fix up any links in standard "node header" (i.e. DepsNode struct, that all - * all others are derived from) that are now corrupt - */ - { - /* relationships to other nodes... */ - // FIXME: how to handle links? We may only have partial set of all nodes still? - // XXX: the exact details of how to handle this are really part of the querying API... - - // XXX: BUT, for copying subgraphs, we'll need to define an API for doing this stuff anyways - // (i.e. for resolving and patching over links that exist within subtree...) - dst->inlinks.clear(); - dst->outlinks.clear(); - - /* clear traversal data */ - dst->num_links_pending = 0; - dst->lasttime = 0; - } - - /* fix links */ - // XXX... -#endif - - /* return copied node */ - return dst; -} +#include "intern/depsgraph_intern.h" bool DEG_id_type_tagged(Main *bmain, short idtype) { @@ -207,7 +57,9 @@ short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) return 0; } - IDDepsNode *id_node = graph->find_id_node(id); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + + DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); if (id_node == NULL) { /* TODO(sergey): Does it mean we need to check set scene? */ return 0; diff --git a/source/blender/depsgraph/intern/depsgraph_queue.cc b/source/blender/depsgraph/intern/depsgraph_queue.cc deleted file mode 100644 index da60d73bc46..00000000000 --- a/source/blender/depsgraph/intern/depsgraph_queue.cc +++ /dev/null @@ -1,177 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2013 Blender Foundation. - * All rights reserved. - * - * Original Author: Joshua Leung - * Contributor(s): None Yet - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/intern/depsgraph_queue.cc - * \ingroup depsgraph - * - * Implementation of special queue type for use in Depsgraph traversals. - */ - -#include <stdlib.h> - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "BLI_utildefines.h" -#include "BLI_heap.h" -#include "BLI_ghash.h" -} /* extern "C" */ - -#include "depsgraph_queue.h" - -/* ****************************** */ -/* Depsgraph Queue implementation */ - -/* Data Management ----------------------------------------- */ - -DepsgraphQueue *DEG_queue_new(void) -{ - DepsgraphQueue *q = (DepsgraphQueue *)MEM_callocN(sizeof(DepsgraphQueue), "DEG_queue_new()"); - - /* init data structures for use here */ - q->pending_heap = BLI_heap_new(); - q->pending_hash = BLI_ghash_ptr_new("DEG Queue Pending Hash"); - - q->ready_heap = BLI_heap_new(); - - /* init settings */ - q->idx = 0; - q->tot = 0; - - /* return queue */ - return q; -} - -void DEG_queue_free(DepsgraphQueue *q) -{ - /* free data structures */ - BLI_assert(BLI_heap_size(q->pending_heap) == 0); - BLI_assert(BLI_heap_size(q->ready_heap) == 0); - BLI_assert(BLI_ghash_size(q->pending_hash) == 0); - - BLI_heap_free(q->pending_heap, NULL); - BLI_heap_free(q->ready_heap, NULL); - BLI_ghash_free(q->pending_hash, NULL, NULL); - - /* free queue itself */ - MEM_freeN(q); -} - -/* Statistics --------------------------------------------- */ - -/* Get the number of nodes which are we should visit, but are not able to yet */ -size_t DEG_queue_num_pending(DepsgraphQueue *q) -{ - return BLI_heap_size(q->pending_heap); -} - -/* Get the number of nodes which are now ready to be visited */ -size_t DEG_queue_num_ready(DepsgraphQueue *q) -{ - return BLI_heap_size(q->ready_heap); -} - -/* Get total size of queue */ -size_t DEG_queue_size(DepsgraphQueue *q) -{ - return DEG_queue_num_pending(q) + DEG_queue_num_ready(q); -} - -/* Check if queue has any items in it (still passing through) */ -bool DEG_queue_is_empty(DepsgraphQueue *q) -{ - return DEG_queue_size(q) == 0; -} - -/* Queue Operations --------------------------------------- */ - -/** - * Add DepsNode to the queue - * \param dnode: ``(DepsNode *)`` node to add to the queue - * Each node is only added once to the queue; Subsequent pushes - * merely update its status (e.g. moving it from "pending" to "ready") - * \param cost: new "num_links_pending" count for node *after* it has encountered - * via an outlink from the node currently being visited - * (i.e. we're one of the dependencies which may now be able to be processed) - */ -void DEG_queue_push(DepsgraphQueue *q, void *dnode, float cost) -{ - HeapNode *hnode = NULL; - - /* Shortcut: Directly add to ready if node isn't waiting on anything now... */ - if (cost == 0) { - /* node is now ready to be visited - schedule it up for such */ - if (BLI_ghash_haskey(q->pending_hash, dnode)) { - /* remove from pending queue - we're moving it to the scheduling queue */ - hnode = (HeapNode *)BLI_ghash_lookup(q->pending_hash, dnode); - BLI_heap_remove(q->pending_heap, hnode); - - BLI_ghash_remove(q->pending_hash, dnode, NULL, NULL); - } - - /* schedule up node using latest count (of ready nodes) */ - BLI_heap_insert(q->ready_heap, (float)q->idx, dnode); - q->idx++; - } - else { - /* node is still waiting on some other ancestors, - * so add it to the pending heap in the meantime... - */ - // XXX: is this even necessary now? - if (BLI_ghash_haskey(q->pending_hash, dnode)) { - /* just update cost on pending node */ - hnode = (HeapNode *)BLI_ghash_lookup(q->pending_hash, dnode); - BLI_heap_remove(q->pending_heap, hnode); - BLI_heap_insert(q->pending_heap, cost, hnode); - } - else { - /* add new node to pending queue, and increase size of overall queue */ - hnode = BLI_heap_insert(q->pending_heap, cost, dnode); - q->tot++; - } - } -} - -/* Grab a "ready" node from the queue */ -void *DEG_queue_pop(DepsgraphQueue *q) -{ - /* sanity check: if there are no "ready" nodes, - * start pulling from "pending" to keep things moving, - * but throw a warning so that we know that something's up here... - */ - if (BLI_heap_is_empty(q->ready_heap)) { - // XXX: this should never happen - // XXX: if/when it does happen, we may want instead to just wait until something pops up here... - printf("DepsgraphHeap Warning: No more ready nodes available. Trying from pending (idx = %d, tot = %d, pending = %d, ready = %d)\n", - (int)q->idx, (int)q->tot, (int)DEG_queue_num_pending(q), (int)DEG_queue_num_ready(q)); - - return BLI_heap_popmin(q->pending_heap); - } - else { - /* only grab "ready" nodes */ - return BLI_heap_popmin(q->ready_heap); - } -} diff --git a/source/blender/depsgraph/intern/depsgraph_queue.h b/source/blender/depsgraph/intern/depsgraph_queue.h deleted file mode 100644 index b85d46bd173..00000000000 --- a/source/blender/depsgraph/intern/depsgraph_queue.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2013 Blender Foundation. - * All rights reserved. - * - * Original Author: Joshua Leung - * Contributor(s): None Yet - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/intern/depsgraph_queue.h - * \ingroup depsgraph - * - * Defines for special queue type for use in Depsgraph traversals. - */ - -#ifndef __DEPSGRAPH_QUEUE_H__ -#define __DEPSGRAPH_QUEUE_H__ - -struct DepsNode; - -struct Heap; -struct GHash; - -/* *********************************************** */ -/* Dependency Graph Traversal Queue - * - * There are two parts to this: - * a) "Pending" Nodes - This part contains the set of nodes - * which are related to those which have been visited - * previously, but are not yet ready to actually be visited. - * b) "Scheduled" Nodes - These are the nodes whose ancestors - * have all been evaluated already, which means that any - * or all of them can be picked (in practically in order) to - * be visited immediately. - * - * Internally, the queue makes sure that each node in the graph - * only gets added to the queue once. This is because there can - * be multiple inlinks to each node given the way that the relations - * work. - */ - -/* Depsgraph Queue Type */ -typedef struct DepsgraphQueue { - /* Pending */ - struct Heap *pending_heap; /* (valence:int, DepsNode*) */ - struct GHash *pending_hash; /* (DepsNode* : HeapNode*>) */ - - /* Ready to be visited - fifo */ - struct Heap *ready_heap; /* (idx:int, DepsNode*) */ - - /* Size/Order counts */ - size_t idx; /* total number of nodes which are/have been ready so far (including those already visited) */ - size_t tot; /* total number of nodes which have passed through queue; mainly for debug */ -} DepsgraphQueue; - -/* ************************** */ -/* Depsgraph Queue Operations */ - -/* Data management */ -DepsgraphQueue *DEG_queue_new(void); -void DEG_queue_free(DepsgraphQueue *q); - -/* Statistics */ -size_t DEG_queue_num_pending(DepsgraphQueue *q); -size_t DEG_queue_num_ready(DepsgraphQueue *q); - -size_t DEG_queue_size(DepsgraphQueue *q); -bool DEG_queue_is_empty(DepsgraphQueue *q); - -/* Operations */ -void DEG_queue_push(DepsgraphQueue *q, void *dnode, float cost = 0.0f); -void *DEG_queue_pop(DepsgraphQueue *q); - -#endif /* DEPSGRAPH_QUEUE_H */ diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 3ca23965749..ea5afaab3f7 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -55,11 +55,14 @@ extern "C" { #include "DEG_depsgraph.h" } /* extern "C" */ -#include "depsgraph_debug.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" +#include "intern/eval/deg_eval_flush.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" /* *********************** */ /* Update Tagging/Flushing */ @@ -154,19 +157,21 @@ void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag) */ void DEG_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id) { - IDDepsNode *node = graph->find_id_node(id); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + DEG::IDDepsNode *node = deg_graph->find_id_node(id); lib_id_recalc_tag(bmain, id); if (node != NULL) { - node->tag_update(graph); + node->tag_update(deg_graph); } } /* Tag nodes related to a specific piece of data */ void DEG_graph_data_tag_update(Depsgraph *graph, const PointerRNA *ptr) { - DepsNode *node = graph->find_node_from_pointer(ptr, NULL); - if (node) { - node->tag_update(graph); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + DEG::DepsNode *node = deg_graph->find_node_from_pointer(ptr, NULL); + if (node != NULL) { + node->tag_update(deg_graph); } else { printf("Missing node in %s\n", __func__); @@ -179,9 +184,10 @@ void DEG_graph_property_tag_update(Depsgraph *graph, const PointerRNA *ptr, const PropertyRNA *prop) { - DepsNode *node = graph->find_node_from_pointer(ptr, prop); - if (node) { - node->tag_update(graph); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + DEG::DepsNode *node = deg_graph->find_node_from_pointer(ptr, prop); + if (node != NULL) { + node->tag_update(deg_graph); } else { printf("Missing node in %s\n", __func__); @@ -259,131 +265,6 @@ void DEG_id_type_tag(Main *bmain, short idtype) bmain->id_tag_update[((unsigned char *)&idtype)[0]] = 1; } -/* Update Flushing ---------------------------------- */ - -/* FIFO queue for tagged nodes that need flushing */ -/* XXX This may get a dedicated implementation later if needed - lukas */ -typedef std::queue<OperationDepsNode *> FlushQueue; - -static void flush_init_func(void *data_v, int i) -{ - /* ID node's done flag is used to avoid multiple editors update - * for the same ID. - */ - Depsgraph *graph = (Depsgraph *)data_v; - OperationDepsNode *node = graph->operations[i]; - IDDepsNode *id_node = node->owner->owner; - id_node->done = 0; - node->scheduled = false; - node->owner->flags &= ~DEPSCOMP_FULLY_SCHEDULED; -} - -/* Flush updates from tagged nodes outwards until all affected nodes are tagged. */ -void DEG_graph_flush_updates(Main *bmain, Depsgraph *graph) -{ - /* sanity check */ - if (graph == NULL) - return; - - /* Nothing to update, early out. */ - if (graph->entry_tags.size() == 0) { - return; - } - - /* TODO(sergey): With a bit of flag magic we can get rid of this - * extra loop. - */ - const int num_operations = graph->operations.size(); - const bool do_threads = num_operations > 256; - BLI_task_parallel_range(0, num_operations, graph, flush_init_func, do_threads); - - FlushQueue queue; - /* Starting from the tagged "entry" nodes, flush outwards... */ - /* NOTE: Also need to ensure that for each of these, there is a path back to - * root, or else they won't be done. - * NOTE: Count how many nodes we need to handle - entry nodes may be - * component nodes which don't count for this purpose! - */ - for (Depsgraph::EntryTags::const_iterator it = graph->entry_tags.begin(); - it != graph->entry_tags.end(); - ++it) - { - OperationDepsNode *node = *it; - IDDepsNode *id_node = node->owner->owner; - queue.push(node); - if (id_node->done == 0) { - deg_editors_id_update(bmain, id_node->id); - id_node->done = 1; - } - node->scheduled = true; - } - - while (!queue.empty()) { - OperationDepsNode *node = queue.front(); - queue.pop(); - - IDDepsNode *id_node = node->owner->owner; - lib_id_recalc_tag(bmain, id_node->id); - /* TODO(sergey): For until we've got proper data nodes in the graph. */ - lib_id_recalc_data_tag(bmain, id_node->id); - - ID *id = id_node->id; - /* This code is used to preserve those areas which does direct - * object update, - * - * Plus it ensures visibility changes and relations and layers - * visibility update has proper flags to work with. - */ - if (GS(id->name) == ID_OB) { - Object *object = (Object *)id; - ComponentDepsNode *comp_node = node->owner; - if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { - object->recalc |= OB_RECALC_TIME; - } - else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { - object->recalc |= OB_RECALC_OB; - } - else { - object->recalc |= OB_RECALC_DATA; - } - } - - /* Flush to nodes along links... */ - for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); - it != node->outlinks.end(); - ++it) - { - DepsRelation *rel = *it; - OperationDepsNode *to_node = (OperationDepsNode *)rel->to; - if (to_node->scheduled == false) { - to_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - queue.push(to_node); - to_node->scheduled = true; - if (id_node->done == 0) { - deg_editors_id_update(bmain, id_node->id); - id_node->done = 1; - } - } - } - - /* TODO(sergey): For until incremental updates are possible - * witin a component at least we tag the whole component - * for update. - */ - ComponentDepsNode *component = node->owner; - if ((component->flags & DEPSCOMP_FULLY_SCHEDULED) == 0) { - for (ComponentDepsNode::OperationMap::iterator it = component->operations.begin(); - it != node->owner->operations.end(); - ++it) - { - OperationDepsNode *op = it->second; - op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - } - component->flags |= DEPSCOMP_FULLY_SCHEDULED; - } - } -} - /* Recursively push updates out to all nodes dependent on this, * until all affected are tagged and/or scheduled up for eval */ @@ -395,34 +276,17 @@ void DEG_ids_flush_tagged(Main *bmain) { /* TODO(sergey): Only visible scenes? */ if (scene->depsgraph != NULL) { - DEG_graph_flush_updates(bmain, scene->depsgraph); + DEG::deg_graph_flush_updates( + bmain, + reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph)); } } } -static void graph_clear_func(void *data_v, int i) -{ - Depsgraph *graph = (Depsgraph *)data_v; - OperationDepsNode *node = graph->operations[i]; - /* Clear node's "pending update" settings. */ - node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE); -} - -/* Clear tags from all operation nodes. */ -void DEG_graph_clear_tags(Depsgraph *graph) -{ - /* Go over all operation nodes, clearing tags. */ - const int num_operations = graph->operations.size(); - const bool do_threads = num_operations > 256; - BLI_task_parallel_range(0, num_operations, graph, graph_clear_func, do_threads); - /* Clear any entry tags which haven't been flushed. */ - graph->entry_tags.clear(); -} - /* Update dependency graph when visible scenes/layers changes. */ void DEG_graph_on_visible_update(Main *bmain, Scene *scene) { - Depsgraph *graph = scene->depsgraph; + DEG::Depsgraph *graph = reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph); wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; int old_layers = graph->layers; if (wm != NULL) { @@ -450,11 +314,8 @@ void DEG_graph_on_visible_update(Main *bmain, Scene *scene) * This is mainly needed on file load only, after that updates of invisible objects * will be stored in the pending list. */ - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) + GHASH_FOREACH_BEGIN(DEG::IDDepsNode *, id_node, graph->id_hash) { - IDDepsNode *id_node = it->second; ID *id = id_node->id; if ((id->tag & LIB_TAG_ID_RECALC_ALL) != 0 || (id_node->layers & scene->lay_updated) == 0) @@ -472,14 +333,15 @@ void DEG_graph_on_visible_update(Main *bmain, Scene *scene) (object->recalc & OB_RECALC_ALL) != 0) { id_node->tag_update(graph); - ComponentDepsNode *anim_comp = - id_node->find_component(DEPSNODE_TYPE_ANIMATION); + DEG::ComponentDepsNode *anim_comp = + id_node->find_component(DEG::DEPSNODE_TYPE_ANIMATION); if (anim_comp != NULL && object->recalc & OB_RECALC_TIME) { anim_comp->tag_update(graph); } } } } + GHASH_FOREACH_END(); } scene->lay_updated |= graph->layers; } @@ -520,7 +382,7 @@ void DEG_ids_check_recalc(Main *bmain, Scene *scene, bool time) } } - deg_editors_scene_update(bmain, scene, (updated || time)); + DEG::deg_editors_scene_update(bmain, scene, (updated || time)); } void DEG_ids_clear_recalc(Main *bmain) diff --git a/source/blender/depsgraph/intern/depsgraph_type_defines.cc b/source/blender/depsgraph/intern/depsgraph_type_defines.cc index 5a3048a4aa3..97208939e57 100644 --- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc +++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc @@ -30,6 +30,8 @@ * Defines and code for core node types. */ +#include <cstdlib> // for BLI_assert() + extern "C" { #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -37,10 +39,13 @@ extern "C" { #include "DEG_depsgraph.h" } /* extern "C" */ -#include "depsgraph_intern.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" + +namespace DEG { /* ************ */ /* External API */ @@ -59,44 +64,108 @@ static GHash *_depsnode_typeinfo_registry = NULL; /* Registration ------------------------------------------- */ /* Register node type */ -void DEG_register_node_typeinfo(DepsNodeFactory *factory) +void deg_register_node_typeinfo(DepsNodeFactory *factory) { BLI_assert(factory != NULL); BLI_ghash_insert(_depsnode_typeinfo_registry, SET_INT_IN_POINTER(factory->type()), factory); } -/* Register all node types */ -void DEG_register_node_types(void) -{ - /* initialise registry */ - _depsnode_typeinfo_registry = BLI_ghash_int_new("Depsgraph Node Type Registry"); - - /* register node types */ - DEG_register_base_depsnodes(); - DEG_register_component_depsnodes(); - DEG_register_operation_depsnodes(); -} - -/* Free registry on exit */ -void DEG_free_node_types(void) -{ - BLI_ghash_free(_depsnode_typeinfo_registry, NULL, NULL); -} - /* Getters ------------------------------------------------- */ /* Get typeinfo for specified type */ -DepsNodeFactory *DEG_get_node_factory(const eDepsNode_Type type) +DepsNodeFactory *deg_get_node_factory(const eDepsNode_Type type) { /* look up type - at worst, it doesn't exist in table yet, and we fail */ return (DepsNodeFactory *)BLI_ghash_lookup(_depsnode_typeinfo_registry, SET_INT_IN_POINTER(type)); } /* Get typeinfo for provided node */ -DepsNodeFactory *DEG_node_get_factory(const DepsNode *node) +DepsNodeFactory *deg_node_get_factory(const DepsNode *node) { - if (!node) + if (node != NULL) { return NULL; + } + return deg_get_node_factory(node->type); +} + +/* Stringified opcodes ------------------------------------- */ + +DepsOperationStringifier DEG_OPNAMES; - return DEG_get_node_factory(node->type); +static const char *stringify_opcode(eDepsOperation_Code opcode) +{ + switch (opcode) { +#define STRINGIFY_OPCODE(name) case DEG_OPCODE_##name: return #name + STRINGIFY_OPCODE(OPERATION); + STRINGIFY_OPCODE(PLACEHOLDER); + STRINGIFY_OPCODE(NOOP); + STRINGIFY_OPCODE(ANIMATION); + STRINGIFY_OPCODE(DRIVER); + //STRINGIFY_OPCODE(PROXY); + STRINGIFY_OPCODE(TRANSFORM_LOCAL); + STRINGIFY_OPCODE(TRANSFORM_PARENT); + STRINGIFY_OPCODE(TRANSFORM_CONSTRAINTS); + //STRINGIFY_OPCODE(TRANSFORM_CONSTRAINTS_INIT); + //STRINGIFY_OPCODE(TRANSFORM_CONSTRAINT); + //STRINGIFY_OPCODE(TRANSFORM_CONSTRAINTS_DONE); + STRINGIFY_OPCODE(RIGIDBODY_REBUILD); + STRINGIFY_OPCODE(RIGIDBODY_SIM); + STRINGIFY_OPCODE(TRANSFORM_RIGIDBODY); + STRINGIFY_OPCODE(TRANSFORM_FINAL); + STRINGIFY_OPCODE(OBJECT_UBEREVAL); + STRINGIFY_OPCODE(GEOMETRY_UBEREVAL); + STRINGIFY_OPCODE(GEOMETRY_MODIFIER); + STRINGIFY_OPCODE(GEOMETRY_PATH); + STRINGIFY_OPCODE(POSE_INIT); + STRINGIFY_OPCODE(POSE_DONE); + STRINGIFY_OPCODE(POSE_IK_SOLVER); + STRINGIFY_OPCODE(POSE_SPLINE_IK_SOLVER); + STRINGIFY_OPCODE(BONE_LOCAL); + STRINGIFY_OPCODE(BONE_POSE_PARENT); + STRINGIFY_OPCODE(BONE_CONSTRAINTS); + //STRINGIFY_OPCODE(BONE_CONSTRAINTS_INIT); + //STRINGIFY_OPCODE(BONE_CONSTRAINT); + //STRINGIFY_OPCODE(BONE_CONSTRAINTS_DONE); + STRINGIFY_OPCODE(BONE_READY); + STRINGIFY_OPCODE(BONE_DONE); + STRINGIFY_OPCODE(PSYS_EVAL); + + case DEG_NUM_OPCODES: return "SpecialCase"; +#undef STRINGIFY_OPCODE + } + return "UNKNOWN"; +} + +DepsOperationStringifier::DepsOperationStringifier() { + for (int i = 0; i < DEG_NUM_OPCODES; ++i) { + names_[i] = stringify_opcode((eDepsOperation_Code)i); + } +} + +const char *DepsOperationStringifier::operator[](eDepsOperation_Code opcode) { + BLI_assert((opcode > 0) && (opcode < DEG_NUM_OPCODES)); + if (opcode >= 0 && opcode < DEG_NUM_OPCODES) { + return names_[opcode]; + } + return "UnknownOpcode"; +} + +} // namespace DEG + +/* Register all node types */ +void DEG_register_node_types(void) +{ + /* initialise registry */ + DEG::_depsnode_typeinfo_registry = BLI_ghash_int_new("Depsgraph Node Type Registry"); + + /* register node types */ + DEG::deg_register_base_depsnodes(); + DEG::deg_register_component_depsnodes(); + DEG::deg_register_operation_depsnodes(); +} + +/* Free registry on exit */ +void DEG_free_node_types(void) +{ + BLI_ghash_free(DEG::_depsnode_typeinfo_registry, NULL, NULL); } diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h index f5fbf0bcc76..7516ccbfdc2 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -34,10 +34,9 @@ * in the graph. */ -#ifndef __DEPSGRAPH_TYPES_H__ -#define __DEPSGRAPH_TYPES_H__ +#pragma once -#include "depsgraph_util_function.h" +#include "util/deg_util_function.h" /* TODO(sergey): Ideally we'll just use char* and statically allocated strings * to avoid any possible overhead caused by string (re)allocation/formatting. @@ -55,69 +54,232 @@ struct PointerRNA; struct EvaluationContext; struct FCurve; +namespace DEG { + /* Evaluation Operation for atomic operation */ // XXX: move this to another header that can be exposed? typedef function<void(struct EvaluationContext *)> DepsEvalOperationCb; -/* Metatype of Nodes - The general "level" in the graph structure the node serves */ +/* Metatype of Nodes - The general "level" in the graph structure + * the node serves. + */ typedef enum eDepsNode_Class { - DEPSNODE_CLASS_GENERIC = 0, /* Types generally unassociated with user-visible entities, but needed for graph functioning */ - - DEPSNODE_CLASS_COMPONENT = 1, /* [Outer Node] An "aspect" of evaluating/updating an ID-Block, requiring certain types of evaluation behaviours */ - DEPSNODE_CLASS_OPERATION = 2, /* [Inner Node] A glorified function-pointer/callback for scheduling up evaluation operations for components, subject to relationship requirements */ + /* Types generally unassociated with user-visible entities, + * but needed for graph functioning. + */ + DEPSNODE_CLASS_GENERIC = 0, + /* [Outer Node] An "aspect" of evaluating/updating an ID-Block, requiring + * certain types of evaluation behavior. + */ + DEPSNODE_CLASS_COMPONENT = 1, + /* [Inner Node] A glorified function-pointer/callback for scheduling up + * evaluation operations for components, subject to relationship + * requirements. + */ + DEPSNODE_CLASS_OPERATION = 2, } eDepsNode_Class; /* Types of Nodes */ typedef enum eDepsNode_Type { - DEPSNODE_TYPE_UNDEFINED = -1, /* fallback type for invalid return value */ + /* Fallback type for invalid return value */ + DEPSNODE_TYPE_UNDEFINED = -1, + /* Inner Node (Operation) */ + DEPSNODE_TYPE_OPERATION = 0, + + /* **** Generic Types **** */ + + /* "Current Scene" - basically whatever kicks off the evaluation process. */ + DEPSNODE_TYPE_ROOT = 1, + /* Time-Source */ + DEPSNODE_TYPE_TIMESOURCE = 2, + /* ID-Block reference - used as landmarks/collection point for components, + * but not usually part of main graph. + */ + DEPSNODE_TYPE_ID_REF = 3, + /* Isolated sub-graph - used for keeping instanced data separate from + * instances using them. + */ + DEPSNODE_TYPE_SUBGRAPH = 4, - DEPSNODE_TYPE_OPERATION = 0, /* Inner Node (Operation) */ + /* **** Outer Types **** */ - /* Generic Types */ - DEPSNODE_TYPE_ROOT = 1, /* "Current Scene" - basically whatever kicks off the evaluation process */ - DEPSNODE_TYPE_TIMESOURCE = 2, /* Time-Source */ + /* Parameters Component - Default when nothing else fits + * (i.e. just SDNA property setting). + */ + DEPSNODE_TYPE_PARAMETERS = 11, + /* Generic "Proxy-Inherit" Component + * XXX: Also for instancing of subgraphs? + */ + DEPSNODE_TYPE_PROXY = 12, + /* Animation Component + * + * XXX: merge in with parameters? + */ + DEPSNODE_TYPE_ANIMATION = 13, + /* Transform Component (Parenting/Constraints) */ + DEPSNODE_TYPE_TRANSFORM = 14, + /* Geometry Component (DerivedMesh/Displist) */ + DEPSNODE_TYPE_GEOMETRY = 15, + /* Sequencer Component (Scene Only) */ + DEPSNODE_TYPE_SEQUENCER = 16, + + /* **** Evaluation-Related Outer Types (with Subdata) **** */ + + /* Pose Component - Owner/Container of Bones Eval */ + DEPSNODE_TYPE_EVAL_POSE = 21, + /* Bone Component - Child/Subcomponent of Pose */ + DEPSNODE_TYPE_BONE = 22, + /* Particle Systems Component */ + DEPSNODE_TYPE_EVAL_PARTICLES = 23, + /* Material Shading Component */ + DEPSNODE_TYPE_SHADING = 24, +} eDepsNode_Type; - DEPSNODE_TYPE_ID_REF = 3, /* ID-Block reference - used as landmarks/collection point for components, but not usually part of main graph */ - DEPSNODE_TYPE_SUBGRAPH = 4, /* Isolated sub-graph - used for keeping instanced data separate from instances using them */ +/* Identifiers for common operations (as an enum). */ +typedef enum eDepsOperation_Code { + /* Generic Operations ------------------------------ */ - /* Outer Types */ - DEPSNODE_TYPE_PARAMETERS = 11, /* Parameters Component - Default when nothing else fits (i.e. just SDNA property setting) */ - DEPSNODE_TYPE_PROXY = 12, /* Generic "Proxy-Inherit" Component */ // XXX: Also for instancing of subgraphs? - DEPSNODE_TYPE_ANIMATION = 13, /* Animation Component */ // XXX: merge in with parameters? - DEPSNODE_TYPE_TRANSFORM = 14, /* Transform Component (Parenting/Constraints) */ - DEPSNODE_TYPE_GEOMETRY = 15, /* Geometry Component (DerivedMesh/Displist) */ - DEPSNODE_TYPE_SEQUENCER = 16, /* Sequencer Component (Scene Only) */ + /* Placeholder for operations which don't need special mention */ + DEG_OPCODE_OPERATION = 0, - /* Evaluation-Related Outer Types (with Subdata) */ - DEPSNODE_TYPE_EVAL_POSE = 21, /* Pose Component - Owner/Container of Bones Eval */ - DEPSNODE_TYPE_BONE = 22, /* Bone Component - Child/Subcomponent of Pose */ + // XXX: Placeholder while porting depsgraph code + DEG_OPCODE_PLACEHOLDER, - DEPSNODE_TYPE_EVAL_PARTICLES = 23, /* Particle Systems Component */ - DEPSNODE_TYPE_SHADING = 24, /* Material Shading Component */ -} eDepsNode_Type; + DEG_OPCODE_NOOP, -/* Identifiers for common operations (as an enum) */ -typedef enum eDepsOperation_Code { -#define DEF_DEG_OPCODE(label) DEG_OPCODE_##label, -#include "depsnode_opcodes.h" -#undef DEF_DEG_OPCODE + /* Animation, Drivers, etc. ------------------------ */ + + /* NLA + Action */ + DEG_OPCODE_ANIMATION, + + /* Driver */ + DEG_OPCODE_DRIVER, + + /* Proxy Inherit? */ + //DEG_OPCODE_PROXY, + + /* Transform --------------------------------------- */ + + /* Transform entry point - local transforms only */ + DEG_OPCODE_TRANSFORM_LOCAL, + + /* Parenting */ + DEG_OPCODE_TRANSFORM_PARENT, + + /* Constraints */ + DEG_OPCODE_TRANSFORM_CONSTRAINTS, + //DEG_OPCODE_TRANSFORM_CONSTRAINTS_INIT, + //DEG_OPCODE_TRANSFORM_CONSTRAINT, + //DEG_OPCODE_TRANSFORM_CONSTRAINTS_DONE, + + /* Rigidbody Sim - Perform Sim */ + DEG_OPCODE_RIGIDBODY_REBUILD, + DEG_OPCODE_RIGIDBODY_SIM, + + /* Rigidbody Sim - Copy Results to Object */ + DEG_OPCODE_TRANSFORM_RIGIDBODY, + + /* Transform exitpoint */ + DEG_OPCODE_TRANSFORM_FINAL, + + /* XXX: ubereval is for temporary porting purposes only */ + DEG_OPCODE_OBJECT_UBEREVAL, + + /* Geometry ---------------------------------------- */ + + /* XXX: Placeholder - UberEval */ + DEG_OPCODE_GEOMETRY_UBEREVAL, + + /* Modifier */ + DEG_OPCODE_GEOMETRY_MODIFIER, + + /* Curve Objects - Path Calculation (used for path-following tools, */ + DEG_OPCODE_GEOMETRY_PATH, + + /* Pose -------------------------------------------- */ + + /* Init IK Trees, etc. */ + DEG_OPCODE_POSE_INIT, + + /* Free IK Trees + Compute Deform Matrices */ + DEG_OPCODE_POSE_DONE, + + /* IK/Spline Solvers */ + DEG_OPCODE_POSE_IK_SOLVER, + DEG_OPCODE_POSE_SPLINE_IK_SOLVER, + + /* Bone -------------------------------------------- */ + + /* Bone local transforms - Entrypoint */ + DEG_OPCODE_BONE_LOCAL, + + /* Pose-space conversion (includes parent + restpose, */ + DEG_OPCODE_BONE_POSE_PARENT, + + /* Constraints */ + DEG_OPCODE_BONE_CONSTRAINTS, + //DEG_OPCODE_BONE_CONSTRAINTS_INIT, + //DEG_OPCODE_BONE_CONSTRAINT, + //DEG_OPCODE_BONE_CONSTRAINTS_DONE, + + /* Bone transforms are ready + * + * - "READY" This (internal, noop is used to signal that all pre-IK + * operations are done. Its role is to help mediate situations + * where cyclic relations may otherwise form (i.e. one bone in + * chain targetting another in same chain, + * + * - "DONE" This noop is used to signal that the bone's final pose + * transform can be read by others + */ + // TODO: deform mats could get calculated in the final_transform ops... + DEG_OPCODE_BONE_READY, + DEG_OPCODE_BONE_DONE, + + /* Particles --------------------------------------- */ + + /* XXX: placeholder - Particle System eval */ + DEG_OPCODE_PSYS_EVAL, + + DEG_NUM_OPCODES, } eDepsOperation_Code; -/* String defines for these opcodes, defined in depsnode_operation.cpp */ -extern const char *DEG_OPNAMES[]; +/* Some magic to stringify operation codes. */ +class DepsOperationStringifier { +public: + DepsOperationStringifier(); + const char *operator[](eDepsOperation_Code opcodex); +protected: + const char *names_[DEG_NUM_OPCODES]; +}; +/* String defines for these opcodes, defined in depsgraph_type_defines.cpp */ +extern DepsOperationStringifier DEG_OPNAMES; /* Type of operation */ typedef enum eDepsOperation_Type { - /* Primary operation types */ - DEPSOP_TYPE_INIT = 0, /* initialise evaluation data */ - DEPSOP_TYPE_EXEC = 1, /* standard evaluation step */ - DEPSOP_TYPE_POST = 2, /* cleanup evaluation data + flush results */ - - /* Additional operation types */ - DEPSOP_TYPE_OUT = 3, /* indicator for outputting a temporary result that other components can use */ // XXX? - DEPSOP_TYPE_SIM = 4, /* indicator for things like IK Solvers and Rigidbody Sim steps which modify final results of separate entities at once */ - DEPSOP_TYPE_REBUILD = 5, /* rebuild internal evaluation data - used for Rigidbody Reset and Armature Rebuild-On-Load */ + /* **** Primary operation types **** */ + + /* Initialise evaluation data */ + DEPSOP_TYPE_INIT = 0, + /* Standard evaluation step */ + DEPSOP_TYPE_EXEC = 1, + /* Cleanup evaluation data + flush results */ + DEPSOP_TYPE_POST = 2, + + /* **** Additional operation types **** */ + /* Indicator for outputting a temporary result that other components + * can use. // XXX? + */ + DEPSOP_TYPE_OUT = 3, + /* Indicator for things like IK Solvers and Rigidbody Sim steps which + * modify final results of separate entities at once. + */ + DEPSOP_TYPE_SIM = 4, + /* Rebuild internal evaluation data - used for Rigidbody Reset and + * Armature Rebuild-On-Load. + */ + DEPSOP_TYPE_REBUILD = 5, } eDepsOperation_Type; /* Types of relationships between nodes @@ -170,4 +332,4 @@ typedef enum eDepsRelation_Type { DEPSREL_TYPE_UPDATE_UI, } eDepsRelation_Type; -#endif /* __DEPSGRAPH_TYPES_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_opcodes.h b/source/blender/depsgraph/intern/depsnode_opcodes.h deleted file mode 100644 index b81822c0ac5..00000000000 --- a/source/blender/depsgraph/intern/depsnode_opcodes.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2014 Blender Foundation. - * All rights reserved. - * - * Original Author: Joshua Leung - * Contributor(s): None Yet - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/intern/depsnode_opcodes.h - * \ingroup depsgraph - * - * \par OpCodes for OperationDepsNodes - * - * This file defines all the "operation codes" (opcodes) used to identify - * common operation node types. The intention of these defines is to have - * a fast and reliable way of identifying the relevant nodes within a component - * without having to use fragile dynamic strings. - * - * This file is meant to be used like UI_icons.h. That is, before including - * the file, the host file must define the DEG_OPCODE(_label) macro, which - * is responsible for converting the define into whatever form is suitable. - * Therefore, it intentionally doesn't have header guards. - */ - - -/* Example macro define: */ -/* #define DEF_DEG_OPCODE(label) DEG_OPCODE_##label, */ - -/* Generic Operations ------------------------------ */ - -/* Placeholder for operations which don't need special mention */ -DEF_DEG_OPCODE(OPERATION) - -// XXX: Placeholder while porting depsgraph code -DEF_DEG_OPCODE(PLACEHOLDER) - -DEF_DEG_OPCODE(NOOP) - -/* Animation, Drivers, etc. ------------------------ */ - -/* NLA + Action */ -DEF_DEG_OPCODE(ANIMATION) - -/* Driver */ -DEF_DEG_OPCODE(DRIVER) - -/* Proxy Inherit? */ -//DEF_DEG_OPCODE(PROXY) - -/* Transform --------------------------------------- */ - -/* Transform entry point - local transforms only */ -DEF_DEG_OPCODE(TRANSFORM_LOCAL) - -/* Parenting */ -DEF_DEG_OPCODE(TRANSFORM_PARENT) - -/* Constraints */ -DEF_DEG_OPCODE(TRANSFORM_CONSTRAINTS) -//DEF_DEG_OPCODE(TRANSFORM_CONSTRAINTS_INIT) -//DEF_DEG_OPCODE(TRANSFORM_CONSTRAINT) -//DEF_DEG_OPCODE(TRANSFORM_CONSTRAINTS_DONE) - -/* Rigidbody Sim - Perform Sim */ -DEF_DEG_OPCODE(RIGIDBODY_REBUILD) -DEF_DEG_OPCODE(RIGIDBODY_SIM) - -/* Rigidbody Sim - Copy Results to Object */ -DEF_DEG_OPCODE(TRANSFORM_RIGIDBODY) - -/* Transform exitpoint */ -DEF_DEG_OPCODE(TRANSFORM_FINAL) - -/* XXX: ubereval is for temporary porting purposes only */ -DEF_DEG_OPCODE(OBJECT_UBEREVAL) - -/* Geometry ---------------------------------------- */ - -/* XXX: Placeholder - UberEval */ -DEF_DEG_OPCODE(GEOMETRY_UBEREVAL) - -/* Modifier */ -DEF_DEG_OPCODE(GEOMETRY_MODIFIER) - -/* Curve Objects - Path Calculation (used for path-following tools) */ -DEF_DEG_OPCODE(GEOMETRY_PATH) - -/* Pose -------------------------------------------- */ - -/* Init IK Trees, etc. */ -DEF_DEG_OPCODE(POSE_INIT) - -/* Free IK Trees + Compute Deform Matrices */ -DEF_DEG_OPCODE(POSE_DONE) - -/* IK/Spline Solvers */ -DEF_DEG_OPCODE(POSE_IK_SOLVER) -DEF_DEG_OPCODE(POSE_SPLINE_IK_SOLVER) - -/* Bone -------------------------------------------- */ - -/* Bone local transforms - Entrypoint */ -DEF_DEG_OPCODE(BONE_LOCAL) - -/* Pose-space conversion (includes parent + restpose) */ -DEF_DEG_OPCODE(BONE_POSE_PARENT) - -/* Constraints */ -DEF_DEG_OPCODE(BONE_CONSTRAINTS) -//DEF_DEG_OPCODE(BONE_CONSTRAINTS_INIT) -//DEF_DEG_OPCODE(BONE_CONSTRAINT) -//DEF_DEG_OPCODE(BONE_CONSTRAINTS_DONE) - -/* Bone transforms are ready - * - "READY" This (internal) noop is used to signal that all pre-IK operations are done. - * Its role is to help mediate situations where cyclic relations may otherwise form - * (i.e. one bone in chain targetting another in same chain) - * - "DONE" This noop is used to signal that the bone's final pose transform can be read by others - */ -// TODO: deform mats could get calculated in the final_transform ops... -DEF_DEG_OPCODE(BONE_READY) -DEF_DEG_OPCODE(BONE_DONE) - -/* Particles --------------------------------------- */ - -/* XXX: placeholder - Particle System eval */ -DEF_DEG_OPCODE(PSYS_EVAL) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc new file mode 100644 index 00000000000..198cd349002 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -0,0 +1,409 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/depsgraph_eval.cc + * \ingroup depsgraph + * + * Evaluation engine entrypoints for Depsgraph Engine. + */ + +#include "intern/eval/deg_eval.h" + +#include "PIL_time.h" + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_task.h" +#include "BLI_ghash.h" + +#include "BKE_depsgraph.h" +#include "BKE_global.h" + +#include "DEG_depsgraph.h" +} /* extern "C" */ + +#include "atomic_ops.h" + +#include "intern/eval/deg_eval_debug.h" +#include "intern/eval/deg_eval_flush.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph.h" +#include "util/deg_util_foreach.h" + +/* Unfinished and unused, and takes quite some pre-processing time. */ +#undef USE_EVAL_PRIORITY + +/* Use integrated debugger to keep track how much each of the nodes was + * evaluating. + */ +#undef USE_DEBUGGER + +namespace DEG { + +/* ********************** */ +/* Evaluation Entrypoints */ + +/* Forward declarations. */ +static void schedule_children(TaskPool *pool, + Depsgraph *graph, + OperationDepsNode *node, + const int layers, + const int thread_id); + +struct DepsgraphEvalState { + EvaluationContext *eval_ctx; + Depsgraph *graph; + int layers; +}; + +static void deg_task_run_func(TaskPool *pool, + void *taskdata, + int thread_id) +{ + DepsgraphEvalState *state = + reinterpret_cast<DepsgraphEvalState *>(BLI_task_pool_userdata(pool)); + OperationDepsNode *node = reinterpret_cast<OperationDepsNode *>(taskdata); + + BLI_assert(!node->is_noop() && "NOOP nodes should not actually be scheduled"); + + /* Should only be the case for NOOPs, which never get to this point. */ + BLI_assert(node->evaluate); + + while (true) { + /* Get context. */ + /* TODO: Who initialises this? "Init" operations aren't able to + * initialise it!!! + */ + /* TODO(sergey): We don't use component contexts at this moment. */ + /* ComponentDepsNode *comp = node->owner; */ + BLI_assert(node->owner != NULL); + + /* Since we're not leaving the thread for until the graph branches it is + * possible to have NO-OP on the way. for which evaluate() will be NULL. + * but that's all fine, we'll just scheduler it's children. + */ + if (node->evaluate) { + /* Take note of current time. */ +#ifdef USE_DEBUGGER + double start_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_started(state->graph, node); +#endif + + /* Perform operation. */ + node->evaluate(state->eval_ctx); + + /* Note how long this took. */ +#ifdef USE_DEBUGGER + double end_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_completed(state->graph, + node, + end_time - start_time); +#endif + } + + /* If there's only one outgoing link we try to immediately switch to + * that node evaluation, without leaving the thread. + * + * It's only doable if the child don't have extra relations or all they + * are satisfied. + * + * TODO(sergey): Checks here can be de-duplicated with the ones from + * schedule_node(), however, how to do it nicely? + */ + if (node->outlinks.size() == 1) { + DepsRelation *rel = node->outlinks[0]; + OperationDepsNode *child = (OperationDepsNode *)rel->to; + BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); + if (!child->scheduled) { + int id_layers = child->owner->owner->layers; + if (!((child->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 && + (id_layers & state->layers) != 0)) + { + /* Node does not need an update, so can;t continue with the + * chain and need to switch to another one by leaving the + * thread. + */ + break; + } + if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { + BLI_assert(child->num_links_pending > 0); + atomic_sub_uint32(&child->num_links_pending, 1); + } + if (child->num_links_pending == 0) { + bool is_scheduled = atomic_fetch_and_or_uint8( + (uint8_t *)&child->scheduled, (uint8_t)true); + if (!is_scheduled) { + /* Node was not scheduled, switch to it! */ + node = child; + } + else { + /* Someone else scheduled the node, leaving us + * unemployed in this thread, we're leaving. + */ + break; + } + } + else { + /* There are other dependencies on the child, can't do + * anything in the current thread. + */ + break; + } + } + else { + /* Happens when having cyclic dependencies. + * + * Nothing to do here, single child was already scheduled, we + * can leave the thread now. + */ + break; + } + } + else { + /* TODO(sergey): It's possible to use one of the outgoing relations + * as a chain which we'll try to keep alive, but it's a bit more + * involved change. + */ + schedule_children(pool, state->graph, node, state->layers, thread_id); + break; + } + } +} + +typedef struct CalculatePengindData { + Depsgraph *graph; + int layers; +} CalculatePengindData; + +static void calculate_pending_func(void *data_v, int i) +{ + CalculatePengindData *data = (CalculatePengindData *)data_v; + Depsgraph *graph = data->graph; + int layers = data->layers; + OperationDepsNode *node = graph->operations[i]; + IDDepsNode *id_node = node->owner->owner; + + node->num_links_pending = 0; + node->scheduled = false; + + /* count number of inputs that need updates */ + if ((id_node->layers & layers) != 0 && + (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) + { + foreach (DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEPSNODE_TYPE_OPERATION && + (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) + { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + IDDepsNode *id_from_node = from->owner->owner; + if ((id_from_node->layers & layers) != 0 && + (from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) + { + ++node->num_links_pending; + } + } + } + } +} + +static void calculate_pending_parents(Depsgraph *graph, int layers) +{ + const int num_operations = graph->operations.size(); + const bool do_threads = num_operations > 256; + CalculatePengindData data; + data.graph = graph; + data.layers = layers; + BLI_task_parallel_range(0, + num_operations, + &data, + calculate_pending_func, + do_threads); +} + +#ifdef USE_EVAL_PRIORITY +static void calculate_eval_priority(OperationDepsNode *node) +{ + if (node->done) { + return; + } + node->done = 1; + + if (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { + /* XXX standard cost of a node, could be estimated somewhat later on */ + const float cost = 1.0f; + /* NOOP nodes have no cost */ + node->eval_priority = node->is_noop() ? cost : 0.0f; + + foreach (DepsRelation *rel, node->outlinks) { + OperationDepsNode *to = (OperationDepsNode *)rel->to; + BLI_assert(to->type == DEPSNODE_TYPE_OPERATION); + calculate_eval_priority(to); + node->eval_priority += to->eval_priority; + } + } + else { + node->eval_priority = 0.0f; + } +} +#endif + +/* Schedule a node if it needs evaluation. + * dec_parents: Decrement pending parents count, true when child nodes are + * scheduled after a task has been completed. + */ +static void schedule_node(TaskPool *pool, Depsgraph *graph, int layers, + OperationDepsNode *node, bool dec_parents, + const int thread_id) +{ + int id_layers = node->owner->owner->layers; + + if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 && + (id_layers & layers) != 0) + { + if (dec_parents) { + BLI_assert(node->num_links_pending > 0); + atomic_sub_uint32(&node->num_links_pending, 1); + } + + if (node->num_links_pending == 0) { + bool is_scheduled = atomic_fetch_and_or_uint8( + (uint8_t *)&node->scheduled, (uint8_t)true); + if (!is_scheduled) { + if (node->is_noop()) { + /* skip NOOP node, schedule children right away */ + schedule_children(pool, graph, node, layers, thread_id); + } + else { + /* children are scheduled once this task is completed */ + BLI_task_pool_push_from_thread(pool, + deg_task_run_func, + node, + false, + TASK_PRIORITY_LOW, + thread_id); + } + } + } + } +} + +static void schedule_graph(TaskPool *pool, + Depsgraph *graph, + const int layers) +{ + foreach (OperationDepsNode *node, graph->operations) { + schedule_node(pool, graph, layers, node, false, 0); + } +} + +static void schedule_children(TaskPool *pool, + Depsgraph *graph, + OperationDepsNode *node, + const int layers, + const int thread_id) +{ + foreach (DepsRelation *rel, node->outlinks) { + OperationDepsNode *child = (OperationDepsNode *)rel->to; + BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); + if (child->scheduled) { + /* Happens when having cyclic dependencies. */ + continue; + } + schedule_node(pool, + graph, + layers, + child, + (rel->flag & DEPSREL_FLAG_CYCLIC) == 0, + thread_id); + } +} + +/** + * Evaluate all nodes tagged for updating, + * \warning This is usually done as part of main loop, but may also be + * called from frame-change update. + * + * \note Time sources should be all valid! + */ +void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, + Depsgraph *graph, + const int layers) +{ + /* Generate base evaluation context, upon which all the others are derived. */ + // TODO: this needs both main and scene access... + + /* Nothing to update, early out. */ + if (BLI_gset_size(graph->entry_tags) == 0) { + return; + } + + /* Set time for the current graph evaluation context. */ + TimeSourceDepsNode *time_src = graph->find_time_source(); + eval_ctx->ctime = time_src->cfra; + + /* XXX could use a separate pool for each eval context */ + DepsgraphEvalState state; + state.eval_ctx = eval_ctx; + state.graph = graph; + state.layers = layers; + + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &state); + + if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { + BLI_pool_set_num_threads(task_pool, 1); + } + + calculate_pending_parents(graph, layers); + + /* Clear tags. */ + foreach (OperationDepsNode *node, graph->operations) { + node->done = 0; + } + + /* Calculate priority for operation nodes. */ +#ifdef USE_EVAL_PRIORITY + foreach (OperationDepsNode *node, graph->operations) { + calculate_eval_priority(node); + } +#endif + + DepsgraphDebug::eval_begin(eval_ctx); + + schedule_graph(task_pool, graph, layers); + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + DepsgraphDebug::eval_end(eval_ctx); + + /* Clear any uncleared tags - just in case. */ + deg_graph_clear_tags(graph); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h new file mode 100644 index 00000000000..0d42f63433f --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval.h @@ -0,0 +1,52 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/eval/deg_eval.cc + * \ingroup depsgraph + * + * Evaluation engine entrypoints for Depsgraph Engine. + */ + +#pragma once + +struct EvaluationContext; + +namespace DEG { + +struct Depsgraph; + +/** + * Evaluate all nodes tagged for updating, + * \warning This is usually done as part of main loop, but may also be + * called from frame-change update. + * + * \note Time sources should be all valid! + */ +void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, + Depsgraph *graph, + const int layers); + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc new file mode 100644 index 00000000000..67d64aae8bf --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc @@ -0,0 +1,249 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 Blender Foundation. + * All rights reserved. + * + * Original Author: Lukas Toenne + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/eval/deg_eval_debug.cc + * \ingroup depsgraph + * + * Implementation of tools for debugging the depsgraph + */ + +#include <cstring> + +#include "intern/eval/deg_eval_debug.h" + +extern "C" { +#include "BLI_listbase.h" +#include "BLI_ghash.h" + +#include "DEG_depsgraph_debug.h" + +#include "WM_api.h" +#include "WM_types.h" +} /* extern "C" */ + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_intern.h" + +namespace DEG { + +DepsgraphStats *DepsgraphDebug::stats = NULL; + +static string get_component_name(eDepsNode_Type type, const string &name = "") +{ + DepsNodeFactory *factory = deg_get_node_factory(type); + if (name.empty()) { + return string(factory->tname()); + } + else { + return string(factory->tname()) + " | " + name; + } +} + +static void times_clear(DepsgraphStatsTimes ×) +{ + times.duration_last = 0.0f; +} + +static void times_add(DepsgraphStatsTimes ×, float time) +{ + times.duration_last += time; +} + +void DepsgraphDebug::eval_begin(const EvaluationContext *UNUSED(eval_ctx)) +{ + /* TODO(sergey): Stats are currently globally disabled. */ + /* verify_stats(); */ + reset_stats(); +} + +void DepsgraphDebug::eval_end(const EvaluationContext *UNUSED(eval_ctx)) +{ + WM_main_add_notifier(NC_SPACE | ND_SPACE_INFO_REPORT, NULL); +} + +void DepsgraphDebug::eval_step(const EvaluationContext *UNUSED(eval_ctx), + const char *message) +{ +#ifdef DEG_DEBUG_BUILD + if (deg_debug_eval_cb) + deg_debug_eval_cb(deg_debug_eval_userdata, message); +#else + (void)message; /* Ignored. */ +#endif +} + +void DepsgraphDebug::task_started(Depsgraph *graph, + const OperationDepsNode *node) +{ + if (stats) { + BLI_spin_lock(&graph->lock); + + ComponentDepsNode *comp = node->owner; + ID *id = comp->owner->id; + + DepsgraphStatsID *id_stats = get_id_stats(id, true); + times_clear(id_stats->times); + + /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ + if (0) { + /* XXX component name usage needs cleanup! currently mixes identifier + * and description strings! + */ + DepsgraphStatsComponent *comp_stats = + get_component_stats(id, get_component_name(comp->type, + comp->name), + true); + times_clear(comp_stats->times); + } + + BLI_spin_unlock(&graph->lock); + } +} + +void DepsgraphDebug::task_completed(Depsgraph *graph, + const OperationDepsNode *node, + double time) +{ + if (stats) { + BLI_spin_lock(&graph->lock); + + ComponentDepsNode *comp = node->owner; + ID *id = comp->owner->id; + + DepsgraphStatsID *id_stats = get_id_stats(id, true); + times_add(id_stats->times, time); + + /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ + if (0) { + /* XXX component name usage needs cleanup! currently mixes identifier + * and description strings! + */ + DepsgraphStatsComponent *comp_stats = + get_component_stats(id, + get_component_name(comp->type, + comp->name), + true); + times_add(comp_stats->times, time); + } + + BLI_spin_unlock(&graph->lock); + } +} + +/* ********** */ +/* Statistics */ + + +/* GHash callback */ +static void deg_id_stats_free(void *val) +{ + DepsgraphStatsID *id_stats = (DepsgraphStatsID *)val; + + if (id_stats) { + BLI_freelistN(&id_stats->components); + MEM_freeN(id_stats); + } +} + +void DepsgraphDebug::stats_init() +{ + if (!stats) { + stats = (DepsgraphStats *)MEM_callocN(sizeof(DepsgraphStats), + "Depsgraph Stats"); + stats->id_stats = BLI_ghash_new(BLI_ghashutil_ptrhash, + BLI_ghashutil_ptrcmp, + "Depsgraph ID Stats Hash"); + } +} + +void DepsgraphDebug::stats_free() +{ + if (stats) { + BLI_ghash_free(stats->id_stats, NULL, deg_id_stats_free); + MEM_freeN(stats); + stats = NULL; + } +} + +void DepsgraphDebug::verify_stats() +{ + stats_init(); +} + +void DepsgraphDebug::reset_stats() +{ + if (!stats) { + return; + } + + /* XXX this doesn't work, will immediately clear all info, + * since most depsgraph updates have none or very few updates to handle. + * + * Could consider clearing only zero-user ID blocks here + */ +// BLI_ghash_clear(stats->id_stats, NULL, deg_id_stats_free); +} + +DepsgraphStatsID *DepsgraphDebug::get_id_stats(ID *id, bool create) +{ + DepsgraphStatsID *id_stats = (DepsgraphStatsID *)BLI_ghash_lookup(stats->id_stats, id); + + if (!id_stats && create) { + id_stats = (DepsgraphStatsID *)MEM_callocN(sizeof(DepsgraphStatsID), + "Depsgraph ID Stats"); + id_stats->id = id; + + BLI_ghash_insert(stats->id_stats, id, id_stats); + } + + return id_stats; +} + +DepsgraphStatsComponent *DepsgraphDebug::get_component_stats( + DepsgraphStatsID *id_stats, + const string &name, + bool create) +{ + DepsgraphStatsComponent *comp_stats; + for (comp_stats = (DepsgraphStatsComponent *)id_stats->components.first; + comp_stats != NULL; + comp_stats = comp_stats->next) + { + if (STREQ(comp_stats->name, name.c_str())) + break; + } + if (!comp_stats && create) { + comp_stats = (DepsgraphStatsComponent *)MEM_callocN(sizeof(DepsgraphStatsComponent), + "Depsgraph Component Stats"); + BLI_strncpy(comp_stats->name, name.c_str(), sizeof(comp_stats->name)); + BLI_addtail(&id_stats->components, comp_stats); + } + return comp_stats; +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_debug.h b/source/blender/depsgraph/intern/eval/deg_eval_debug.h index 64b97855f57..9109019eb2d 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_debug.h @@ -24,27 +24,26 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_debug.h +/** \file blender/depsgraph/intern/eval/deg_eval_debug.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_DEBUG_H__ -#define __DEPSGRAPH_DEBUG_H__ +#pragma once -#include "depsgraph_types.h" +#include "intern/depsgraph_types.h" -extern "C" { -#include "BKE_global.h" -} +struct ID; +struct EvaluationContext; struct DepsgraphStats; struct DepsgraphStatsID; struct DepsgraphStatsComponent; -struct DepsgraphSettings; -struct EvaluationContext; -struct OperationDepsNode; + +namespace DEG { struct Depsgraph; +struct DepsgraphSettings; +struct OperationDepsNode; struct DepsgraphDebug { static DepsgraphStats *stats; @@ -77,11 +76,4 @@ struct DepsgraphDebug { } }; -#define DEG_DEBUG_PRINTF(...) \ - { \ - if (G.debug & G_DEBUG_DEPSGRAPH) { \ - fprintf(stderr, __VA_ARGS__); \ - } \ - } \ - -#endif /* __DEPSGRAPH_DEBUG_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc new file mode 100644 index 00000000000..af68f5c55c4 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -0,0 +1,224 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/depsgraph_tag.cc + * \ingroup depsgraph + * + * Core routines for how the Depsgraph works. + */ + +#include "intern/eval/deg_eval_flush.h" + +// TODO(sergey): Use some sort of wrapper. +#include <queue> + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_utildefines.h" +#include "BLI_task.h" +#include "BLI_ghash.h" + +#include "DEG_depsgraph.h" +} /* extern "C" */ + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +namespace DEG { + +namespace { + +// TODO(sergey): De-duplicate with depsgraph_tag,cc +void lib_id_recalc_tag(Main *bmain, ID *id) +{ + id->tag |= LIB_TAG_ID_RECALC; + DEG_id_type_tag(bmain, GS(id->name)); +} + +void lib_id_recalc_data_tag(Main *bmain, ID *id) +{ + id->tag |= LIB_TAG_ID_RECALC_DATA; + DEG_id_type_tag(bmain, GS(id->name)); +} + +} /* namespace */ + +typedef std::queue<OperationDepsNode *> FlushQueue; + +static void flush_init_func(void *data_v, int i) +{ + /* ID node's done flag is used to avoid multiple editors update + * for the same ID. + */ + Depsgraph *graph = (Depsgraph *)data_v; + OperationDepsNode *node = graph->operations[i]; + IDDepsNode *id_node = node->owner->owner; + id_node->done = 0; + node->scheduled = false; + node->owner->flags &= ~DEPSCOMP_FULLY_SCHEDULED; +} + +/* Flush updates from tagged nodes outwards until all affected nodes + * are tagged. + */ +void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) +{ + /* Sanity check. */ + if (graph == NULL) { + return; + } + + /* Nothing to update, early out. */ + if (BLI_gset_size(graph->entry_tags) == 0) { + return; + } + + /* TODO(sergey): With a bit of flag magic we can get rid of this + * extra loop. + */ + const int num_operations = graph->operations.size(); + const bool do_threads = num_operations > 256; + BLI_task_parallel_range(0, + num_operations, + graph, + flush_init_func, + do_threads); + + FlushQueue queue; + /* Starting from the tagged "entry" nodes, flush outwards... */ + /* NOTE: Also need to ensure that for each of these, there is a path back to + * root, or else they won't be done. + * NOTE: Count how many nodes we need to handle - entry nodes may be + * component nodes which don't count for this purpose! + */ + GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags) + { + queue.push(node); + node->scheduled = true; + } + GSET_FOREACH_END(); + + while (!queue.empty()) { + OperationDepsNode *node = queue.front(); + queue.pop(); + + for (;;) { + node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + + IDDepsNode *id_node = node->owner->owner; + + if (id_node->done == 0) { + deg_editors_id_update(bmain, id_node->id); + id_node->done = 1; + } + + lib_id_recalc_tag(bmain, id_node->id); + /* TODO(sergey): For until we've got proper data nodes in the graph. */ + lib_id_recalc_data_tag(bmain, id_node->id); + + ID *id = id_node->id; + /* This code is used to preserve those areas which does direct + * object update, + * + * Plus it ensures visibility changes and relations and layers + * visibility update has proper flags to work with. + */ + if (GS(id->name) == ID_OB) { + Object *object = (Object *)id; + ComponentDepsNode *comp_node = node->owner; + if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { + object->recalc |= OB_RECALC_TIME; + } + else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { + object->recalc |= OB_RECALC_OB; + } + else { + object->recalc |= OB_RECALC_DATA; + } + } + + /* TODO(sergey): For until incremental updates are possible + * witin a component at least we tag the whole component + * for update. + */ + ComponentDepsNode *component = node->owner; + if ((component->flags & DEPSCOMP_FULLY_SCHEDULED) == 0) { + foreach (OperationDepsNode *op, component->operations) { + op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + } + component->flags |= DEPSCOMP_FULLY_SCHEDULED; + } + + /* Flush to nodes along links... */ + if (node->outlinks.size() == 1) { + OperationDepsNode *to_node = (OperationDepsNode *)node->outlinks[0]->to; + if (to_node->scheduled == false) { + to_node->scheduled = true; + node = to_node; + } + else { + break; + } + } + else { + foreach (DepsRelation *rel, node->outlinks) { + OperationDepsNode *to_node = (OperationDepsNode *)rel->to; + if (to_node->scheduled == false) { + queue.push(to_node); + to_node->scheduled = true; + } + } + break; + } + } + } +} + +static void graph_clear_func(void *data_v, int i) +{ + Depsgraph *graph = (Depsgraph *)data_v; + OperationDepsNode *node = graph->operations[i]; + /* Clear node's "pending update" settings. */ + node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE); +} + +/* Clear tags from all operation nodes. */ +void deg_graph_clear_tags(Depsgraph *graph) +{ + /* Go over all operation nodes, clearing tags. */ + const int num_operations = graph->operations.size(); + const bool do_threads = num_operations > 256; + BLI_task_parallel_range(0, num_operations, graph, graph_clear_func, do_threads); + /* Clear any entry tags which haven't been flushed. */ + BLI_gset_clear(graph->entry_tags, NULL); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h new file mode 100644 index 00000000000..8912aebee7d --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h @@ -0,0 +1,49 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/eval/deg_eval_flush.cc + * \ingroup depsgraph + * + * Core routines for how the Depsgraph works. + */ + +#pragma once + +struct Main; + +namespace DEG { + +struct Depsgraph; + +/* Flush updates from tagged nodes outwards until all affected nodes + * are tagged. + */ +void deg_graph_flush_updates(struct Main *bmain, struct Depsgraph *graph); + +/* Clear tags from all operation nodes. */ +void deg_graph_clear_tags(struct Depsgraph *graph); + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode.cc b/source/blender/depsgraph/intern/nodes/deg_node.cc index 8aa1059d2dc..78293f7f483 100644 --- a/source/blender/depsgraph/intern/depsnode.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node.cc @@ -28,10 +28,13 @@ * \ingroup depsgraph */ +#include "intern/nodes/deg_node.h" + #include <stdio.h> #include <string.h> #include "BLI_utildefines.h" +#include "BLI_ghash.h" extern "C" { #include "DNA_ID.h" @@ -42,10 +45,13 @@ extern "C" { #include "DEG_depsgraph.h" } -#include "depsnode.h" /* own include */ -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" +#include "util/deg_util_hash.h" + +namespace DEG { /* *************** */ /* Node Management */ @@ -66,7 +72,7 @@ DepsNode::TypeInfo::TypeInfo(eDepsNode_Type type, const char *tname) DepsNode::DepsNode() { - this->name[0] = '\0'; + name[0] = '\0'; } DepsNode::~DepsNode() @@ -76,11 +82,9 @@ DepsNode::~DepsNode() * when we're trying to free same link from both it's sides. We don't have * dangling links so this is not a problem from memory leaks point of view. */ - DEPSNODE_RELATIONS_ITER_BEGIN(this->inlinks, rel) - { + foreach (DepsRelation *rel, inlinks) { OBJECT_GUARDED_DELETE(rel, DepsRelation); } - DEPSNODE_RELATIONS_ITER_END; } @@ -100,11 +104,7 @@ string DepsNode::identifier() const void TimeSourceDepsNode::tag_update(Depsgraph *graph) { - for (DepsNode::Relations::const_iterator it = outlinks.begin(); - it != outlinks.end(); - ++it) - { - DepsRelation *rel = *it; + foreach (DepsRelation *rel, outlinks) { DepsNode *node = rel->to; node->tag_update(graph); } @@ -125,7 +125,7 @@ RootDepsNode::~RootDepsNode() TimeSourceDepsNode *RootDepsNode::add_time_source(const string &name) { if (!time_source) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_TIMESOURCE); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_TIMESOURCE); time_source = (TimeSourceDepsNode *)factory->create_node(NULL, "", name); /*time_source->owner = this;*/ // XXX } @@ -142,6 +142,36 @@ static DepsNodeFactoryImpl<TimeSourceDepsNode> DNTI_TIMESOURCE; /* ID Node ================================================ */ +static unsigned int id_deps_node_hash_key(const void *key_v) +{ + const IDDepsNode::ComponentIDKey *key = + reinterpret_cast<const IDDepsNode::ComponentIDKey *>(key_v); + return hash_combine(BLI_ghashutil_uinthash(key->type), + BLI_ghashutil_strhash_p(key->name.c_str())); +} + +static bool id_deps_node_hash_key_cmp(const void *a, const void *b) +{ + const IDDepsNode::ComponentIDKey *key_a = + reinterpret_cast<const IDDepsNode::ComponentIDKey *>(a); + const IDDepsNode::ComponentIDKey *key_b = + reinterpret_cast<const IDDepsNode::ComponentIDKey *>(b); + return !(*key_a == *key_b); +} + +static void id_deps_node_hash_key_free(void *key_v) +{ + typedef IDDepsNode::ComponentIDKey ComponentIDKey; + ComponentIDKey *key = reinterpret_cast<ComponentIDKey *>(key_v); + OBJECT_GUARDED_DELETE(key, ComponentIDKey); +} + +static void id_deps_node_hash_value_free(void *value_v) +{ + ComponentDepsNode *comp_node = reinterpret_cast<ComponentDepsNode *>(value_v); + OBJECT_GUARDED_DELETE(comp_node, ComponentDepsNode); +} + /* Initialize 'id' node - from pointer data given. */ void IDDepsNode::init(const ID *id, const string &UNUSED(subdata)) { @@ -151,6 +181,10 @@ void IDDepsNode::init(const ID *id, const string &UNUSED(subdata)) this->layers = (1 << 20) - 1; this->eval_flags = 0; + components = BLI_ghash_new(id_deps_node_hash_key, + id_deps_node_hash_key_cmp, + "Depsgraph id components hash"); + /* NOTE: components themselves are created if/when needed. * This prevents problems with components getting added * twice if an ID-Ref needs to be created to house it... @@ -161,51 +195,27 @@ void IDDepsNode::init(const ID *id, const string &UNUSED(subdata)) IDDepsNode::~IDDepsNode() { clear_components(); -} - -/* Copy 'id' node. */ -void IDDepsNode::copy(DepsgraphCopyContext *dcc, const IDDepsNode *src) -{ - (void)src; /* Ignored. */ - /* Iterate over items in original hash, adding them to new hash. */ - for (IDDepsNode::ComponentMap::const_iterator it = this->components.begin(); - it != this->components.end(); - ++it) - { - /* Get current <type : component> mapping. */ - ComponentIDKey c_key = it->first; - DepsNode *old_component = it->second; - - /* Make a copy of component. */ - ComponentDepsNode *component = (ComponentDepsNode *)DEG_copy_node(dcc, old_component); - - /* Add new node to hash... */ - this->components[c_key] = component; - } - - // TODO: perform a second loop to fix up links? - BLI_assert(!"Not expected to be used"); + BLI_ghash_free(components, id_deps_node_hash_key_free, NULL); } ComponentDepsNode *IDDepsNode::find_component(eDepsNode_Type type, const string &name) const { ComponentIDKey key(type, name); - ComponentMap::const_iterator it = components.find(key); - return it != components.end() ? it->second : NULL; + return reinterpret_cast<ComponentDepsNode *>(BLI_ghash_lookup(components, &key)); } ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type, const string &name) { - ComponentIDKey key(type, name); ComponentDepsNode *comp_node = find_component(type, name); if (!comp_node) { - DepsNodeFactory *factory = DEG_get_node_factory(type); + DepsNodeFactory *factory = deg_get_node_factory(type); comp_node = (ComponentDepsNode *)factory->create_node(this->id, "", name); /* Register. */ - this->components[key] = comp_node; + ComponentIDKey *key = OBJECT_GUARDED_NEW(ComponentIDKey, type, name); + BLI_ghash_insert(components, key, comp_node); comp_node->owner = this; } return comp_node; @@ -213,34 +223,28 @@ ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type, void IDDepsNode::remove_component(eDepsNode_Type type, const string &name) { - ComponentIDKey key(type, name); ComponentDepsNode *comp_node = find_component(type, name); if (comp_node) { /* Unregister. */ - this->components.erase(key); - OBJECT_GUARDED_DELETE(comp_node, ComponentDepsNode); + ComponentIDKey key(type, name); + BLI_ghash_remove(components, + &key, + id_deps_node_hash_key_free, + id_deps_node_hash_value_free); } } void IDDepsNode::clear_components() { - for (ComponentMap::const_iterator it = components.begin(); - it != components.end(); - ++it) - { - ComponentDepsNode *comp_node = it->second; - OBJECT_GUARDED_DELETE(comp_node, ComponentDepsNode); - } - components.clear(); + BLI_ghash_clear(components, + id_deps_node_hash_key_free, + id_deps_node_hash_value_free); } void IDDepsNode::tag_update(Depsgraph *graph) { - for (ComponentMap::const_iterator it = components.begin(); - it != components.end(); - ++it) + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components) { - ComponentDepsNode *comp_node = it->second; /* TODO(sergey): What about drievrs? */ bool do_component_tag = comp_node->type != DEPSNODE_TYPE_ANIMATION; if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { @@ -254,6 +258,16 @@ void IDDepsNode::tag_update(Depsgraph *graph) comp_node->tag_update(graph); } } + GHASH_FOREACH_END(); +} + +void IDDepsNode::finalize_build() +{ + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components) + { + comp_node->finalize_build(); + } + GHASH_FOREACH_END(); } DEG_DEPSNODE_DEFINE(IDDepsNode, DEPSNODE_TYPE_ID_REF, "ID Node"); @@ -281,31 +295,21 @@ SubgraphDepsNode::~SubgraphDepsNode() // XXX: prune these flags a bit... if ((this->flag & SUBGRAPH_FLAG_FIRSTREF) || !(this->flag & SUBGRAPH_FLAG_SHARED)) { /* Free the referenced graph. */ - DEG_graph_free(this->graph); - this->graph = NULL; + DEG_graph_free(reinterpret_cast< ::Depsgraph* >(graph)); + graph = NULL; } } -/* Copy 'subgraph' node - Assume that the subgraph doesn't get copied for now... */ -void SubgraphDepsNode::copy(DepsgraphCopyContext * /*dcc*/, - const SubgraphDepsNode * /*src*/) -{ - //const SubgraphDepsNode *src_node = (const SubgraphDepsNode *)src; - //SubgraphDepsNode *dst_node = (SubgraphDepsNode *)dst; - - /* for now, subgraph itself isn't copied... */ - BLI_assert(!"Not expected to be used"); -} - DEG_DEPSNODE_DEFINE(SubgraphDepsNode, DEPSNODE_TYPE_SUBGRAPH, "Subgraph Node"); static DepsNodeFactoryImpl<SubgraphDepsNode> DNTI_SUBGRAPH; - -void DEG_register_base_depsnodes() +void deg_register_base_depsnodes() { - DEG_register_node_typeinfo(&DNTI_ROOT); - DEG_register_node_typeinfo(&DNTI_TIMESOURCE); + deg_register_node_typeinfo(&DNTI_ROOT); + deg_register_node_typeinfo(&DNTI_TIMESOURCE); - DEG_register_node_typeinfo(&DNTI_ID_REF); - DEG_register_node_typeinfo(&DNTI_SUBGRAPH); + deg_register_node_typeinfo(&DNTI_ID_REF); + deg_register_node_typeinfo(&DNTI_SUBGRAPH); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode.h b/source/blender/depsgraph/intern/nodes/deg_node.h index 4a464955384..d79d3d2348d 100644 --- a/source/blender/depsgraph/intern/depsnode.h +++ b/source/blender/depsgraph/intern/nodes/deg_node.h @@ -28,21 +28,18 @@ * \ingroup depsgraph */ -#ifndef __DEPSNODE_H__ -#define __DEPSNODE_H__ +#pragma once -#include "depsgraph_types.h" - -#include "depsgraph_util_hash.h" -#include "depsgraph_util_map.h" -#include "depsgraph_util_set.h" +#include "intern/depsgraph_types.h" struct ID; +struct GHash; struct Scene; +namespace DEG { + struct Depsgraph; struct DepsRelation; -struct DepsgraphCopyContext; struct OperationDepsNode; /* *********************************** */ @@ -94,8 +91,6 @@ struct DepsNode { virtual void init(const ID * /*id*/, const string &/*subdata*/) {} - virtual void copy(DepsgraphCopyContext * /*dcc*/, - const DepsNode * /*src*/) {} virtual void tag_update(Depsgraph * /*graph*/) {} @@ -160,24 +155,7 @@ struct IDDepsNode : public DepsNode { string name; }; - /* XXX can't specialize std::hash for this purpose, because ComponentIDKey is - * a nested type ... - * - * http://stackoverflow.com/a/951245 - */ - struct component_key_hash { - bool operator() (const ComponentIDKey &key) const - { - return hash_combine(hash<int>()(key.type), hash<string>()(key.name)); - } - }; - - typedef unordered_map<ComponentIDKey, - ComponentDepsNode *, - component_key_hash> ComponentMap; - void init(const ID *id, const string &subdata); - void copy(DepsgraphCopyContext *dcc, const IDDepsNode *src); ~IDDepsNode(); ComponentDepsNode *find_component(eDepsNode_Type type, @@ -189,11 +167,13 @@ struct IDDepsNode : public DepsNode { void tag_update(Depsgraph *graph); + void finalize_build(); + /* ID Block referenced. */ ID *id; /* Hash to make it faster to look up components. */ - ComponentMap components; + GHash *components; /* Layers of this node with accumulated layers of it's output relations. */ int layers; @@ -210,7 +190,6 @@ struct IDDepsNode : public DepsNode { /* Subgraph Reference. */ struct SubgraphDepsNode : public DepsNode { void init(const ID *id, const string &subdata); - void copy(DepsgraphCopyContext *dcc, const SubgraphDepsNode *src); ~SubgraphDepsNode(); /* Instanced graph. */ @@ -243,6 +222,6 @@ typedef enum eSubgraphRef_Flag { SUBGRAPH_FLAG_FIRSTREF = (1 << 1), } eSubgraphRef_Flag; -void DEG_register_base_depsnodes(); +void deg_register_base_depsnodes(); -#endif /* __DEPSNODE_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc index a47a0d29228..c529e2ecfe6 100644 --- a/source/blender/depsgraph/intern/depsnode_component.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc @@ -28,6 +28,8 @@ * \ingroup depsgraph */ +#include "intern/nodes/deg_node_component.h" + #include <stdio.h> #include <string.h> @@ -39,20 +41,57 @@ extern "C" { #include "BKE_action.h" } /* extern "C" */ -#include "depsnode_component.h" /* own include */ -#include "depsnode_operation.h" -#include "depsgraph_intern.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" +#include "util/deg_util_hash.h" + +namespace DEG { /* *********** */ /* Outer Nodes */ /* Standard Component Methods ============================= */ +static unsigned int comp_node_hash_key(const void *key_v) +{ + const ComponentDepsNode::OperationIDKey *key = + reinterpret_cast<const ComponentDepsNode::OperationIDKey *>(key_v); + return hash_combine(BLI_ghashutil_uinthash(key->opcode), + BLI_ghashutil_strhash_p(key->name.c_str())); +} + +static bool comp_node_hash_key_cmp(const void *a, const void *b) +{ + const ComponentDepsNode::OperationIDKey *key_a = + reinterpret_cast<const ComponentDepsNode::OperationIDKey *>(a); + const ComponentDepsNode::OperationIDKey *key_b = + reinterpret_cast<const ComponentDepsNode::OperationIDKey *>(b); + return !(*key_a == *key_b); +} + +static void comp_node_hash_key_free(void *key_v) +{ + typedef ComponentDepsNode::OperationIDKey OperationIDKey; + OperationIDKey *key = reinterpret_cast<OperationIDKey *>(key_v); + OBJECT_GUARDED_DELETE(key, OperationIDKey); +} + +static void comp_node_hash_value_free(void *value_v) +{ + OperationDepsNode *op_node = reinterpret_cast<OperationDepsNode *>(value_v); + OBJECT_GUARDED_DELETE(op_node, OperationDepsNode); +} + ComponentDepsNode::ComponentDepsNode() : entry_operation(NULL), exit_operation(NULL), - flags(0) + flags(0), + layers(0) { + operations_map = BLI_ghash_new(comp_node_hash_key, + comp_node_hash_key_cmp, + "Depsgraph id hash"); } /* Initialize 'component' node - from pointer data given */ @@ -63,37 +102,15 @@ void ComponentDepsNode::init(const ID * /*id*/, // XXX: maybe this needs a special API? } -/* Copy 'component' node */ -void ComponentDepsNode::copy(DepsgraphCopyContext * /*dcc*/, - const ComponentDepsNode * /*src*/) -{ -#if 0 // XXX: remove all this - /* duplicate list of operation nodes */ - this->operations.clear(); - - for (OperationMap::const_iterator it = src->operations.begin(); it != src->operations.end(); ++it) { - const string &pchan_name = it->first; - OperationDepsNode *src_op = it->second; - - /* recursive copy */ - DepsNodeFactory *factory = DEG_node_get_factory(src_op); - OperationDepsNode *dst_op = (OperationDepsNode *)factory->copy_node(dcc, src_op); - this->operations[pchan_name] = dst_op; - - /* fix links... */ - // ... - } - - /* copy evaluation contexts */ - // -#endif - BLI_assert(!"Not expected to be called"); -} - /* Free 'component' node */ ComponentDepsNode::~ComponentDepsNode() { clear_operations(); + if (operations_map != NULL) { + BLI_ghash_free(operations_map, + comp_node_hash_key_free, + comp_node_hash_value_free); + } } string ComponentDepsNode::identifier() const @@ -103,15 +120,17 @@ string ComponentDepsNode::identifier() const char typebuf[7]; sprintf(typebuf, "(%d)", type); - return string(typebuf) + name + " : " + idname; + char layers[7]; + sprintf(layers, "%d", this->layers); + + return string(typebuf) + name + " : " + idname + " (Layers: " + layers + ")"; } OperationDepsNode *ComponentDepsNode::find_operation(OperationIDKey key) const { - OperationMap::const_iterator it = this->operations.find(key); - - if (it != this->operations.end()) { - return it->second; + OperationDepsNode *node = reinterpret_cast<OperationDepsNode *>(BLI_ghash_lookup(operations_map, &key)); + if (node != NULL) { + return node; } else { fprintf(stderr, "%s: find_operation(%s) failed\n", @@ -129,11 +148,7 @@ OperationDepsNode *ComponentDepsNode::find_operation(eDepsOperation_Code opcode, OperationDepsNode *ComponentDepsNode::has_operation(OperationIDKey key) const { - OperationMap::const_iterator it = this->operations.find(key); - if (it != this->operations.end()) { - return it->second; - } - return NULL; + return reinterpret_cast<OperationDepsNode *>(BLI_ghash_lookup(operations_map, &key)); } OperationDepsNode *ComponentDepsNode::has_operation(eDepsOperation_Code opcode, @@ -147,12 +162,12 @@ OperationDepsNode *ComponentDepsNode::add_operation(eDepsOperation_Type optype, { OperationDepsNode *op_node = has_operation(opcode, name); if (!op_node) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_OPERATION); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_OPERATION); op_node = (OperationDepsNode *)factory->create_node(this->owner->id, "", name); /* register opnode in this component's operation set */ - OperationIDKey key(opcode, name); - this->operations[key] = op_node; + OperationIDKey *key = OBJECT_GUARDED_NEW(OperationIDKey, opcode, name); + BLI_ghash_insert(operations_map, key, op_node); /* set as entry/exit node of component (if appropriate) */ if (optype == DEPSOP_TYPE_INIT) { @@ -185,18 +200,22 @@ OperationDepsNode *ComponentDepsNode::add_operation(eDepsOperation_Type optype, void ComponentDepsNode::remove_operation(eDepsOperation_Code opcode, const string &name) { - OperationDepsNode *op_node = find_operation(opcode, name); - if (op_node) { - /* unregister */ - this->operations.erase(OperationIDKey(opcode, name)); - OBJECT_GUARDED_DELETE(op_node, OperationDepsNode); - } + /* unregister */ + OperationIDKey key(opcode, name); + BLI_ghash_remove(operations_map, + &key, + comp_node_hash_key_free, + comp_node_hash_key_free); } void ComponentDepsNode::clear_operations() { - for (OperationMap::const_iterator it = operations.begin(); it != operations.end(); ++it) { - OperationDepsNode *op_node = it->second; + if (operations_map != NULL) { + BLI_ghash_clear(operations_map, + comp_node_hash_key_free, + comp_node_hash_value_free); + } + foreach (OperationDepsNode *op_node, operations) { OBJECT_GUARDED_DELETE(op_node, OperationDepsNode); } operations.clear(); @@ -208,30 +227,71 @@ void ComponentDepsNode::tag_update(Depsgraph *graph) if (entry_op != NULL && entry_op->flag & DEPSOP_FLAG_NEEDS_UPDATE) { return; } - for (OperationMap::const_iterator it = operations.begin(); it != operations.end(); ++it) { - OperationDepsNode *op_node = it->second; + foreach (OperationDepsNode *op_node, operations) { op_node->tag_update(graph); } } OperationDepsNode *ComponentDepsNode::get_entry_operation() { - if (entry_operation) + if (entry_operation) { return entry_operation; - else if (operations.size() == 1) - return operations.begin()->second; + } + else if (operations_map != NULL && BLI_ghash_size(operations_map) == 1) { + OperationDepsNode *op_node = NULL; + /* TODO(sergey): This is somewhat slow. */ + GHASH_FOREACH_BEGIN(OperationDepsNode *, tmp, operations_map) + { + op_node = tmp; + } + GHASH_FOREACH_END(); + /* Cache for the subsequent usage. */ + entry_operation = op_node; + return op_node; + } + else if(operations.size() == 1) { + return operations[0]; + } return NULL; } OperationDepsNode *ComponentDepsNode::get_exit_operation() { - if (exit_operation) + if (exit_operation) { return exit_operation; - else if (operations.size() == 1) - return operations.begin()->second; + } + else if (operations_map != NULL && BLI_ghash_size(operations_map) == 1) { + OperationDepsNode *op_node = NULL; + /* TODO(sergey): This is somewhat slow. */ + GHASH_FOREACH_BEGIN(OperationDepsNode *, tmp, operations_map) + { + op_node = tmp; + } + GHASH_FOREACH_END(); + /* Cache for the subsequent usage. */ + exit_operation = op_node; + return op_node; + } + else if(operations.size() == 1) { + return operations[0]; + } return NULL; } +void ComponentDepsNode::finalize_build() +{ + operations.reserve(BLI_ghash_size(operations_map)); + GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, operations_map) + { + operations.push_back(op_node); + } + GHASH_FOREACH_END(); + BLI_ghash_free(operations_map, + comp_node_hash_key_free, + NULL); + operations_map = NULL; +} + /* Parameter Component Defines ============================ */ DEG_DEPSNODE_DEFINE(ParametersComponentDepsNode, DEPSNODE_TYPE_PARAMETERS, "Parameters Component"); @@ -302,18 +362,20 @@ static DepsNodeFactoryImpl<ShadingComponentDepsNode> DNTI_SHADING; /* Node Types Register =================================== */ -void DEG_register_component_depsnodes() +void deg_register_component_depsnodes() { - DEG_register_node_typeinfo(&DNTI_PARAMETERS); - DEG_register_node_typeinfo(&DNTI_PROXY); - DEG_register_node_typeinfo(&DNTI_ANIMATION); - DEG_register_node_typeinfo(&DNTI_TRANSFORM); - DEG_register_node_typeinfo(&DNTI_GEOMETRY); - DEG_register_node_typeinfo(&DNTI_SEQUENCER); - - DEG_register_node_typeinfo(&DNTI_EVAL_POSE); - DEG_register_node_typeinfo(&DNTI_BONE); - - DEG_register_node_typeinfo(&DNTI_EVAL_PARTICLES); - DEG_register_node_typeinfo(&DNTI_SHADING); + deg_register_node_typeinfo(&DNTI_PARAMETERS); + deg_register_node_typeinfo(&DNTI_PROXY); + deg_register_node_typeinfo(&DNTI_ANIMATION); + deg_register_node_typeinfo(&DNTI_TRANSFORM); + deg_register_node_typeinfo(&DNTI_GEOMETRY); + deg_register_node_typeinfo(&DNTI_SEQUENCER); + + deg_register_node_typeinfo(&DNTI_EVAL_POSE); + deg_register_node_typeinfo(&DNTI_BONE); + + deg_register_node_typeinfo(&DNTI_EVAL_PARTICLES); + deg_register_node_typeinfo(&DNTI_SHADING); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h index 7f44c0ed03f..df321ea9299 100644 --- a/source/blender/depsgraph/intern/depsnode_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -28,21 +28,22 @@ * \ingroup depsgraph */ -#ifndef __DEPSNODE_COMPONENT_H__ -#define __DEPSNODE_COMPONENT_H__ +#pragma once -#include "depsnode.h" +#include "intern/nodes/deg_node.h" -#include "depsgraph_util_hash.h" -#include "depsgraph_util_map.h" -#include "depsgraph_util_set.h" +#include "BLI_utildefines.h" +#include "BLI_string.h" struct ID; struct bPoseChannel; +struct GHash; -struct Depsgraph; -struct DepsgraphCopyContext; struct EvaluationContext; + +namespace DEG { + +struct Depsgraph; struct OperationDepsNode; struct BoneComponentDepsNode; @@ -75,7 +76,7 @@ struct ComponentDepsNode : public DepsNode { string identifier() const { char codebuf[5]; - sprintf(codebuf, "%d", opcode); + BLI_snprintf(codebuf, sizeof(codebuf), "%d", opcode); return string("OperationIDKey(") + codebuf + ", " + name + ")"; } @@ -86,47 +87,41 @@ struct ComponentDepsNode : public DepsNode { } }; - /* XXX can't specialize std::hash for this purpose, because ComponentKey is a nested type ... - * http://stackoverflow.com/a/951245 - */ - struct operation_key_hash { - bool operator() (const OperationIDKey &key) const - { - return hash_combine(hash<int>()(key.opcode), hash<string>()(key.name)); - } - }; - /* Typedef for container of operations */ - typedef unordered_map<OperationIDKey, OperationDepsNode *, operation_key_hash> OperationMap; - - ComponentDepsNode(); ~ComponentDepsNode(); void init(const ID *id, const string &subdata); - void copy(DepsgraphCopyContext *dcc, const ComponentDepsNode *src); string identifier() const; /* Find an existing operation, will throw an assert() if it does not exist. */ OperationDepsNode *find_operation(OperationIDKey key) const; - OperationDepsNode *find_operation(eDepsOperation_Code opcode, const string &name) const; + OperationDepsNode *find_operation(eDepsOperation_Code opcode, + const string &name) const; /* Check operation exists and return it. */ OperationDepsNode *has_operation(OperationIDKey key) const; - OperationDepsNode *has_operation(eDepsOperation_Code opcode, const string &name) const; + OperationDepsNode *has_operation(eDepsOperation_Code opcode, + const string &name) const; /** * Create a new node for representing an operation and add this to graph - * \warning If an existing node is found, it will be modified. This helps when node may - * have been partially created earlier (e.g. parent ref before parent item is added) + * \warning If an existing node is found, it will be modified. This helps + * when node may have been partially created earlier (e.g. parent ref before + * parent item is added) * - * \param type: Operation node type (corresponding to context/component that it operates in) - * \param optype: Role that operation plays within component (i.e. where in eval process) + * \param type: Operation node type (corresponding to context/component that + * it operates in) + * \param optype: Role that operation plays within component + * (i.e. where in eval process) * \param op: The operation to perform * \param name: Identifier for operation - used to find/locate it again */ - OperationDepsNode *add_operation(eDepsOperation_Type optype, DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &name); + OperationDepsNode *add_operation(eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string &name); void remove_operation(eDepsOperation_Code opcode, const string &name); void clear_operations(); @@ -135,9 +130,13 @@ struct ComponentDepsNode : public DepsNode { /* Evaluation Context Management .................. */ - /* Initialize component's evaluation context used for the specified purpose */ + /* Initialize component's evaluation context used for the specified + * purpose. + */ virtual bool eval_context_init(EvaluationContext * /*eval_ctx*/) { return false; } - /* Free data in component's evaluation context which is used for the specified purpose + /* Free data in component's evaluation context which is used for + * the specified purpose + * * NOTE: this does not free the actual context in question */ virtual void eval_context_free(EvaluationContext * /*eval_ctx*/) {} @@ -145,15 +144,31 @@ struct ComponentDepsNode : public DepsNode { OperationDepsNode *get_entry_operation(); OperationDepsNode *get_exit_operation(); + void finalize_build(); + IDDepsNode *owner; - OperationMap operations; /* inner nodes for this component */ + /* ** Inner nodes for this component ** */ + + /* Operations stored as a hash map, for faster build. + * This hash map will be freed when graph is fully built. + */ + GHash *operations_map; + + /* This is a "normal" list of operations, used by evaluation + * and other routines after construction. + */ + vector<OperationDepsNode *> operations; + OperationDepsNode *entry_operation; OperationDepsNode *exit_operation; // XXX: a poll() callback to check if component's first node can be started? int flags; + + /* Temporary bitmask, used during graph construction. */ + int layers; }; /* ---------------------------------------- */ @@ -204,6 +219,6 @@ struct ShadingComponentDepsNode : public ComponentDepsNode { }; -void DEG_register_component_depsnodes(); +void deg_register_component_depsnodes(); -#endif /* __DEPSNODE_COMPONENT_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_operation.cc b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc index 6aeb163356b..a9f9703bb3b 100644 --- a/source/blender/depsgraph/intern/depsnode_operation.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc @@ -28,35 +28,27 @@ * \ingroup depsgraph */ +#include "intern/nodes/deg_node_operation.h" + #include "MEM_guardedalloc.h" extern "C" { #include "BLI_utildefines.h" } /* extern "C" */ -#include "depsnode_operation.h" /* own include */ -#include "depsnode_component.h" -#include "depsgraph.h" -#include "depsgraph_intern.h" - -/* ******************************************************************* */ -/* OpNode Identifiers Array - Exported to other depsgraph files too... */ +#include "intern/depsgraph.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_hash.h" -/* identifiers for operations */ -const char *DEG_OPNAMES[] = { -#define DEF_DEG_OPCODE(label) #label, -#include "depsnode_opcodes.h" -#undef DEF_DEG_OPCODE - - "<Invalid>" -}; +namespace DEG { /* *********** */ /* Inner Nodes */ OperationDepsNode::OperationDepsNode() : eval_priority(0.0f), - flag(0) + flag(0), + customdata_mask(0) { } @@ -66,7 +58,6 @@ OperationDepsNode::~OperationDepsNode() string OperationDepsNode::identifier() const { - BLI_assert((opcode > 0) && (opcode < ARRAY_SIZE(DEG_OPNAMES))); return string(DEG_OPNAMES[opcode]) + "(" + name + ")"; } @@ -98,7 +89,9 @@ void OperationDepsNode::tag_update(Depsgraph *graph) DEG_DEPSNODE_DEFINE(OperationDepsNode, DEPSNODE_TYPE_OPERATION, "Operation"); static DepsNodeFactoryImpl<OperationDepsNode> DNTI_OPERATION; -void DEG_register_operation_depsnodes() +void deg_register_operation_depsnodes() { - DEG_register_node_typeinfo(&DNTI_OPERATION); + deg_register_node_typeinfo(&DNTI_OPERATION); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_operation.h b/source/blender/depsgraph/intern/nodes/deg_node_operation.h index 1119e10805d..f03078fc3db 100644 --- a/source/blender/depsgraph/intern/depsnode_operation.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_operation.h @@ -28,15 +28,15 @@ * \ingroup depsgraph */ -#ifndef __DEPSNODE_OPERATION_H__ -#define __DEPSNODE_OPERATION_H__ +#pragma once -#include "depsnode.h" +#include "intern/nodes/deg_node.h" struct ID; struct Depsgraph; -struct DepsgraphCopyContext; + +namespace DEG { /* Flags for Depsgraph Nodes */ typedef enum eDepsOperation_Flag { @@ -44,10 +44,14 @@ typedef enum eDepsOperation_Flag { DEPSOP_FLAG_NEEDS_UPDATE = (1 << 0), /* node was directly modified, causing need for update */ - /* XXX: intention is to make it easier to tell when we just need to take subgraphs */ + /* XXX: intention is to make it easier to tell when we just need to + * take subgraphs. + */ DEPSOP_FLAG_DIRECTLY_MODIFIED = (1 << 1), - /* Operation is evaluated using CPython; has GIL and security implications... */ + /* Operation is evaluated using CPython; has GIL and security + * implications... + */ DEPSOP_FLAG_USES_PYTHON = (1 << 2), } eDepsOperation_Flag; @@ -68,23 +72,33 @@ struct OperationDepsNode : public DepsNode { OperationDepsNode *get_entry_operation() { return this; } OperationDepsNode *get_exit_operation() { return this; } - ComponentDepsNode *owner; /* component that contains the operation */ + /* Component that contains the operation. */ + ComponentDepsNode *owner; - DepsEvalOperationCb evaluate; /* callback for operation */ + /* Callback for operation. */ + DepsEvalOperationCb evaluate; - uint32_t num_links_pending; /* how many inlinks are we still waiting on before we can be evaluated... */ + /* How many inlinks are we still waiting on before we can be evaluated. */ + uint32_t num_links_pending; float eval_priority; bool scheduled; - short optype; /* (eDepsOperation_Type) stage of evaluation */ - int opcode; /* (eDepsOperation_Code) identifier for the operation being performed */ + /* Stage of evaluation */ + eDepsOperation_Type optype; + + /* Identifier for the operation being performed. */ + eDepsOperation_Code opcode; + + /* (eDepsOperation_Flag) extra settings affecting evaluation. */ + int flag; - int flag; /* (eDepsOperation_Flag) extra settings affecting evaluation */ + /* Extra customdata mask which needs to be evaluated for the object. */ + uint64_t customdata_mask; DEG_DEPSNODE_DECLARE; }; -void DEG_register_operation_depsnodes(); +void deg_register_operation_depsnodes(); -#endif /* __DEPSNODE_OPERATION_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/util/deg_util_foreach.h b/source/blender/depsgraph/util/deg_util_foreach.h new file mode 100644 index 00000000000..14cf4fc11ed --- /dev/null +++ b/source/blender/depsgraph/util/deg_util_foreach.h @@ -0,0 +1,68 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Original Author: Sergey Sharybin + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/util/deg_util_foreach.h + * \ingroup depsgraph + */ + +#pragma once + +#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800) +# define foreach(x, y) for(x : y) +#elif defined(HAVE_BOOST_FUNCTION_BINDINGS) +# include <boost/foreach.hpp> +# define foreach BOOST_FOREACH +#else +#pragma message("No available foreach() implementation. Using stub instead, disabling new depsgraph") + +#ifndef WITH_LEGACY_DEPSGRAPH +# error "Unable to build new depsgraph and legacy one is disabled." +#endif + +#define DISABLE_NEW_DEPSGRAPH + +# define foreach(x, y) for (x; false; (void)y) +#endif + +#define GHASH_FOREACH_BEGIN(type, var, what) \ + do { \ + GHashIterator gh_iter##var; \ + GHASH_ITER(gh_iter##var, what) { \ + type var = reinterpret_cast<type>(BLI_ghashIterator_getValue(&gh_iter##var)); \ + +#define GHASH_FOREACH_END() \ + } \ + } while(0) + +#define GSET_FOREACH_BEGIN(type, var, what) \ + do { \ + GSetIterator gh_iter##var; \ + GSET_ITER(gh_iter##var, what) { \ + type var = reinterpret_cast<type>(BLI_gsetIterator_getKey(&gh_iter##var)); \ + +#define GSET_FOREACH_END() \ + } \ + } while(0) diff --git a/source/blender/depsgraph/util/depsgraph_util_function.h b/source/blender/depsgraph/util/deg_util_function.h index a4301833408..1e34ae04d9a 100644 --- a/source/blender/depsgraph/util/depsgraph_util_function.h +++ b/source/blender/depsgraph/util/deg_util_function.h @@ -24,12 +24,11 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_function.h +/** \file blender/depsgraph/util/deg_util_function.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_FUNCTION_H__ -#define __DEPSGRAPH_UTIL_FUNCTION_H__ +#pragma once #if (__cplusplus > 199711L) @@ -57,6 +56,7 @@ using boost::function; #define DISABLE_NEW_DEPSGRAPH +#include "BLI_utildefines.h" #include <cstdlib> template<typename T> @@ -108,5 +108,3 @@ void *function_bind(T func, #define _4 Wrap() #endif - -#endif /* __DEPSGRAPH_UTIL_FUNCTION_H__ */ diff --git a/source/blender/depsgraph/util/depsgraph_util_set.h b/source/blender/depsgraph/util/deg_util_hash.h index 008ec6b74ca..e490be1a7a1 100644 --- a/source/blender/depsgraph/util/depsgraph_util_set.h +++ b/source/blender/depsgraph/util/deg_util_hash.h @@ -24,43 +24,18 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_set.h +/** \file blender/depsgraph/util/deg_util_hash.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_SET_H__ -#define __DEPSGRAPH_UTIL_SET_H__ +#pragma once -#include <set> +#include "BLI_utildefines.h" -#include "depsgraph_util_hash.h" +#include "BLI_ghash.h" -using std::set; - -#if defined(DEG_NO_UNORDERED_MAP) -# include <set> -typedef std::set unordered_set; -#endif - -#if defined(DEG_TR1_UNORDERED_MAP) -# include <tr1/unordered_set> -using std::tr1::unordered_set; -#endif - -#if defined(DEG_STD_UNORDERED_MAP) -# include <unordered_set> -using std::unordered_set; -#endif - -#if defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) -# include <unordered_set> -using std::tr1::unordered_set; -#endif - -#if !defined(DEG_NO_UNORDERED_MAP) && !defined(DEG_TR1_UNORDERED_MAP) && \ - !defined(DEG_STD_UNORDERED_MAP) && !defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) // NOLINT -# error One of: DEG_NO_UNORDERED_MAP, DEG_TR1_UNORDERED_MAP,\ - DEG_STD_UNORDERED_MAP, DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE must be defined! // NOLINT -#endif - -#endif /* __DEPSGRAPH_UTIL_SET_H__ */ +/* XXX this might require 2 different variants for sizeof(size_t) (32 vs 64 bit) */ +BLI_INLINE size_t hash_combine(size_t hash_a, size_t hash_b) +{ + return hash_a ^ (hash_b + 0x9e3779b9 + (hash_a << 6) + (hash_a >> 2)); +} diff --git a/source/blender/depsgraph/util/depsgraph_util_hash.h b/source/blender/depsgraph/util/depsgraph_util_hash.h deleted file mode 100644 index bc75627a026..00000000000 --- a/source/blender/depsgraph/util/depsgraph_util_hash.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2014 Blender Foundation. - * All rights reserved. - * - * Original Author: Brecht van Lommel - * Contributor(s): Lukas Toenne - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/util/depsgraph_util_hash.h - * \ingroup depsgraph - */ - -#ifndef __DEPSGRAPH_UTIL_HASH_H__ -#define __DEPSGRAPH_UTIL_HASH_H__ - -#if defined(DEG_NO_UNORDERED_MAP) -# define DEG_HASH_NAMESPACE_BEGIN -# define DEG_HASH_NAMESPACE_END -#endif - -#if defined(DEG_TR1_UNORDERED_MAP) -# include <tr1/unordered_map> -# define DEG_HASH_NAMESPACE_BEGIN namespace std { namespace tr1 { -# define DEG_HASH_NAMESPACE_END } } -using std::tr1::hash; -#endif - -#if defined(DEG_STD_UNORDERED_MAP) -# include <unordered_map> -# define DEG_HASH_NAMESPACE_BEGIN namespace std { -# define DEG_HASH_NAMESPACE_END } -using std::hash; -#endif - -#if defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) -# include <unordered_map> -# define DEG_HASH_NAMESPACE_BEGIN namespace std { namespace tr1 { -# define DEG_HASH_NAMESPACE_END } } -using std::tr1::hash; -#endif - -#if !defined(DEG_NO_UNORDERED_MAP) && !defined(DEG_TR1_UNORDERED_MAP) && \ - !defined(DEG_STD_UNORDERED_MAP) && !defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) // NOLINT -# error One of: DEG_NO_UNORDERED_MAP, DEG_TR1_UNORDERED_MAP,\ - DEG_STD_UNORDERED_MAP, DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE must be defined! // NOLINT -#endif - -/* XXX this might require 2 different variants for sizeof(size_t) (32 vs 64 bit) */ -inline size_t hash_combine(size_t hash_a, size_t hash_b) -{ - return hash_a ^ (hash_b + 0x9e3779b9 + (hash_a << 6) + (hash_a >> 2)); -} - -#endif /* __DEPSGRAPH_UTIL_HASH_H__ */ diff --git a/source/blender/depsgraph/util/depsgraph_util_map.h b/source/blender/depsgraph/util/depsgraph_util_map.h deleted file mode 100644 index 0eae1d79e34..00000000000 --- a/source/blender/depsgraph/util/depsgraph_util_map.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2014 Blender Foundation. - * All rights reserved. - * - * Original Author: Brecht van Lommel - * Contributor(s): Lukas Toenne - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/util/depsgraph_util_map.h - * \ingroup depsgraph - */ - -#ifndef __DEPSGRAPH_UTIL_MAP_H__ -#define __DEPSGRAPH_UTIL_MAP_H__ - -#include <map> - -#include "depsgraph_util_hash.h" - -using std::map; -using std::pair; - -#if defined(DEG_NO_UNORDERED_MAP) -# include <map> -typedef std::map unordered_map; -#endif - -#if defined(DEG_TR1_UNORDERED_MAP) -# include <tr1/unordered_map> -using std::tr1::unordered_map; -#endif - -#if defined(DEG_STD_UNORDERED_MAP) -# include <unordered_map> -using std::unordered_map; -#endif - -#if defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) -# include <unordered_map> -using std::tr1::unordered_map; -#endif - -#if !defined(DEG_NO_UNORDERED_MAP) && !defined(DEG_TR1_UNORDERED_MAP) && \ - !defined(DEG_STD_UNORDERED_MAP) && !defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) // NOLINT -# error One of: DEG_NO_UNORDERED_MAP, DEG_TR1_UNORDERED_MAP,\ - DEG_STD_UNORDERED_MAP, DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE must be defined! // NOLINT -#endif - -#endif /* __DEPSGRAPH_UTIL_MAP_H__ */ diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 5665ce59783..437dd2b2de2 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -399,7 +399,13 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) ANIM_list_elem_update(ac->scene, ale); } } - + else if (ale->datatype == ALE_NLASTRIP) { + if (ale->update & ANIM_UPDATE_DEPS) { + ale->update &= ~ANIM_UPDATE_DEPS; + ANIM_list_elem_update(ac->scene, ale); + } + } + BLI_assert(ale->update == 0); } } diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index afc4e5c9e61..51f962d4a1e 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -866,7 +866,7 @@ static int add_driver_button_exec(bContext *C, wmOperator *op) } /* Show menu or create drivers */ -static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { PropertyRNA *prop; @@ -877,7 +877,8 @@ static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent * else { /* Show menu */ // TODO: This should get filtered by the enum filter - return WM_menu_invoke(C, op, event); + /* important to execute in the region we're currently in */ + return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_DEFAULT); } } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 6a19cf679be..172f2b9069e 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2024,17 +2024,25 @@ bool autokeyframe_cfra_can_key(Scene *scene, ID *id) /* only filter if auto-key mode requires this */ if (IS_AUTOKEY_ON(scene) == 0) return false; - - if (IS_AUTOKEY_MODE(scene, NORMAL)) { - /* can insert anytime we like... */ - return true; - } - else { /* REPLACE */ - /* for whole block - only key if there's a keyframe on that frame already - * this is a valid assumption when we're blocking + tweaking + + if (IS_AUTOKEY_MODE(scene, EDITKEYS)) { + /* Replace Mode: + * For whole block, only key if there's a keyframe on that frame already + * This is a valid assumption when we're blocking + tweaking */ return id_frame_has_keyframe(id, cfra, ANIMFILTER_KEYS_LOCAL); } + else { + /* Normal Mode (or treat as being normal mode): + * + * Just in case the flags are't set properly (i.e. only on/off is set, without a mode) + * let's set the "normal" flag too, so that it will all be sane everywhere... + */ + scene->toolsettings->autokey_mode = AUTOKEY_MODE_NORMAL; + + /* Can insert anytime we like... */ + return true; + } } /* ******************************************* */ diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index d73536e5ba7..6306926e0b2 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -367,6 +367,8 @@ void transform_armature_mirror_update(Object *obedit) eboflip->tail[2] = ebo->tail[2]; eboflip->rad_tail = ebo->rad_tail; eboflip->roll = -ebo->roll; + eboflip->curveOutX = -ebo->curveOutX; + eboflip->roll2 = -ebo->roll2; /* Also move connected children, in case children's name aren't mirrored properly */ for (children = arm->edbo->first; children; children = children->next) { @@ -382,6 +384,8 @@ void transform_armature_mirror_update(Object *obedit) eboflip->head[2] = ebo->head[2]; eboflip->rad_head = ebo->rad_head; eboflip->roll = -ebo->roll; + eboflip->curveInX = -ebo->curveInX; + eboflip->roll1 = -ebo->roll1; /* Also move connected parent, in case parent's name isn't mirrored properly */ if (eboflip->parent && eboflip->flag & BONE_CONNECTED) { @@ -395,6 +399,11 @@ void transform_armature_mirror_update(Object *obedit) eboflip->roll = -ebo->roll; eboflip->xwidth = ebo->xwidth; eboflip->zwidth = ebo->zwidth; + + eboflip->curveInX = -ebo->curveInX; + eboflip->curveOutX = -ebo->curveOutX; + eboflip->roll1 = -ebo->roll1; + eboflip->roll2 = -ebo->roll2; } } } diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 9e49d7e7e90..2bbd5b8dde8 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2418,7 +2418,7 @@ static void ui_block_colorpicker(uiBlock *block, float rgba[4], PointerRNA *ptr, BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", UNPACK3_EX((unsigned int), rgb_gamma_uchar, )); yco = -3.0f * UI_UNIT_Y; - bt = uiDefBut(block, UI_BTYPE_TEXT, 0, IFACE_("Hex: "), 0, yco, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, TIP_("Hex triplet for color (#RRGGBB)")); + bt = uiDefBut(block, UI_BTYPE_TEXT, 0, IFACE_("Hex: "), 0, yco, butwidth, UI_UNIT_Y, hexcol, 0, 7, 0, 0, TIP_("Hex triplet for color (#RRGGBB)")); UI_but_func_set(bt, ui_colorpicker_hex_rna_cb, bt, hexcol); bt->custom_data = cpicker; uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("(Gamma Corrected)"), 0, yco - UI_UNIT_Y, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index d4c976fb544..acb8e8e7512 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -94,6 +94,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) int triangulate; int use_object_instantiation; + int use_blender_profile; int sort_by_name; int export_transformation_type; int open_sim; @@ -142,6 +143,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) triangulate = RNA_boolean_get(op->ptr, "triangulate"); use_object_instantiation = RNA_boolean_get(op->ptr, "use_object_instantiation"); + use_blender_profile = RNA_boolean_get(op->ptr, "use_blender_profile"); sort_by_name = RNA_boolean_get(op->ptr, "sort_by_name"); export_transformation_type = RNA_enum_get(op->ptr, "export_transformation_type_selection"); open_sim = RNA_boolean_get(op->ptr, "open_sim"); @@ -167,6 +169,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) triangulate, use_object_instantiation, + use_blender_profile, sort_by_name, export_transformation_type, open_sim); @@ -256,6 +259,8 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr) uiItemR(row, imfptr, "triangulate", 0, NULL, ICON_NONE); row = uiLayoutRow(box, false); uiItemR(row, imfptr, "use_object_instantiation", 0, NULL, ICON_NONE); + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "use_blender_profile", 0, NULL, ICON_NONE); row = uiLayoutRow(box, false); split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT); @@ -349,7 +354,10 @@ void WM_OT_collada_export(wmOperatorType *ot) "Export Polygons (Quads & NGons) as Triangles"); RNA_def_boolean(ot->srna, "use_object_instantiation", 1, "Use Object Instances", - "Instantiate multiple Objects from same Data"); + "Instantiate multiple Objects from same Data"); + + RNA_def_boolean(ot->srna, "use_blender_profile", 1, "Use Blender Profile", + "Export additional Blender specific information (for material, shaders, bones, etc.)"); RNA_def_boolean(ot->srna, "sort_by_name", 0, "Sort by Object name", "Sort exported data by Object name"); diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 0280f662a26..8783367ef7e 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -55,6 +55,7 @@ set(SRC editmesh_rip_edge.c editmesh_select.c editmesh_tools.c + editmesh_undo.c editmesh_utils.c mesh_data.c mesh_ops.c diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c new file mode 100644 index 00000000000..e247ea5dce2 --- /dev/null +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -0,0 +1,726 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mesh/editmesh_undo.c + * \ingroup edmesh + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_key_types.h" + +#include "BLI_listbase.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_context.h" +#include "BKE_key.h" +#include "BKE_mesh.h" +#include "BKE_editmesh.h" + +#include "ED_mesh.h" +#include "ED_util.h" + +#define USE_ARRAY_STORE + +#ifdef USE_ARRAY_STORE +// # define DEBUG_PRINT +// # define DEBUG_TIME +# ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +# endif + +# include "BLI_array_store.h" +# include "BLI_math_base.h" + /* check on best size later... */ +# define ARRAY_CHUNK_SIZE 256 + +# define USE_ARRAY_STORE_THREAD +#endif + +#ifdef USE_ARRAY_STORE_THREAD +# include "BLI_task.h" +#endif + + +#ifdef USE_ARRAY_STORE + +/* Single linked list of layers stored per type */ +typedef struct BArrayCustomData { + struct BArrayCustomData *next; + CustomDataType type; + int states_len; /* number of layers for each type */ + BArrayState *states[0]; +} BArrayCustomData; + +#endif + +typedef struct UndoMesh { + Mesh me; + int selectmode; + + /** \note + * this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]), + * but editing shape keys, going into object mode, removing or changing their order, + * then go back into editmode and undo will give issues - where the old index will be out of sync + * with the new object index. + * + * There are a few ways this could be made to work but for now its a known limitation with mixing + * object and editmode operations - Campbell */ + int shapenr; + +#ifdef USE_ARRAY_STORE + /* NULL arrays are considered empty */ + struct { + /* most data is stored as 'custom' data */ + BArrayCustomData *vdata, *edata, *ldata, *pdata; + BArrayState **keyblocks; + BArrayState *mselect; + } store; +#endif /* USE_ARRAY_STORE */ +} UndoMesh; + + +#ifdef USE_ARRAY_STORE + +/** \name Array Store + * \{ */ + +static struct { + BArrayStore **bs_all; + int bs_all_len; + int users; + + /* We could have the undo API pass in the previous state, for now store a local list */ + ListBase local_links; + +#ifdef USE_ARRAY_STORE_THREAD + TaskPool *task_pool; +#endif + +} um_arraystore = {NULL}; + +static BArrayStore *array_store_at_size_ensure(const int stride) +{ + if (um_arraystore.bs_all_len < stride) { + um_arraystore.bs_all_len = stride; + um_arraystore.bs_all = MEM_recallocN(um_arraystore.bs_all, sizeof(*um_arraystore.bs_all) * stride); + } + BArrayStore **bs_p = &um_arraystore.bs_all[stride - 1]; + + if ((*bs_p) == NULL) { +#if 0 + unsigned int chunk_count = ARRAY_CHUNK_SIZE; +#else + /* calculate best chunk-count to fit a power of two */ + unsigned int chunk_count = ARRAY_CHUNK_SIZE; + { + unsigned int size = chunk_count * stride; + size = power_of_2_max_u(size); + size = MEM_SIZE_OPTIMAL(size); + chunk_count = size / stride; + } +#endif + + (*bs_p) = BLI_array_store_create(stride, chunk_count); + } + return *bs_p; +} + +static BArrayStore *array_store_at_size_get(const int stride) +{ + BLI_assert(stride > 0 && stride <= um_arraystore.bs_all_len); + return um_arraystore.bs_all[stride - 1]; +} + +#ifdef DEBUG_PRINT +static void um_arraystore_memory_usage(size_t *r_size_expanded, size_t *r_size_compacted) +{ + size_t size_compacted = 0; + size_t size_expanded = 0; + for (int i = 0; i < um_arraystore.bs_all_len; i++) { + BArrayStore *bs = um_arraystore.bs_all[i]; + if (bs) { + size_compacted += BLI_array_store_calc_size_compacted_get(bs); + size_expanded += BLI_array_store_calc_size_expanded_get(bs); + } + } + + *r_size_expanded = size_expanded; + *r_size_compacted = size_compacted; +} +#endif + +static void um_arraystore_cd_compact( + struct CustomData *cdata, const size_t data_len, + bool create, + const BArrayCustomData *bcd_reference, + BArrayCustomData **r_bcd_first) +{ + if (data_len == 0) { + if (create) { + *r_bcd_first = NULL; + } + } + + const BArrayCustomData *bcd_reference_current = bcd_reference; + BArrayCustomData *bcd = NULL, *bcd_first = NULL, *bcd_prev = NULL; + for (int layer_start = 0, layer_end; layer_start < cdata->totlayer; layer_start = layer_end) { + const CustomDataType type = cdata->layers[layer_start].type; + + layer_end = layer_start + 1; + while ((layer_end < cdata->totlayer) && + (type == cdata->layers[layer_end].type)) + { + layer_end++; + } + + const int stride = CustomData_sizeof(type); + BArrayStore *bs = create ? array_store_at_size_ensure(stride) : NULL; + const int layer_len = layer_end - layer_start; + + if (create) { + if (bcd_reference_current && (bcd_reference_current->type == type)) { + /* common case, the reference is aligned */ + } + else { + bcd_reference_current = NULL; + + /* do a full lookup when un-alligned */ + if (bcd_reference) { + const BArrayCustomData *bcd_iter = bcd_reference; + while (bcd_iter) { + if (bcd_iter->type == type) { + bcd_reference_current = bcd_iter; + break; + } + bcd_iter = bcd_iter->next; + } + } + } + } + + if (create) { + bcd = MEM_callocN(sizeof(BArrayCustomData) + (layer_len * sizeof(BArrayState *)), __func__); + bcd->next = NULL; + bcd->type = type; + bcd->states_len = layer_end - layer_start; + + if (bcd_prev) { + bcd_prev->next = bcd; + bcd_prev = bcd; + } + else { + bcd_first = bcd; + bcd_prev = bcd; + } + } + + CustomDataLayer *layer = &cdata->layers[layer_start]; + for (int i = 0; i < layer_len; i++, layer++) { + if (create) { + if (layer->data) { + BArrayState *state_reference = + (bcd_reference_current && i < bcd_reference_current->states_len) ? + bcd_reference_current->states[i] : NULL; + bcd->states[i] = BLI_array_store_state_add( + bs, layer->data, (size_t)data_len * stride, state_reference); + } + else { + bcd->states[i] = NULL; + } + } + + if (layer->data) { + MEM_freeN(layer->data); + layer->data = NULL; + } + } + + if (create) { + if (bcd_reference_current) { + bcd_reference_current = bcd_reference_current->next; + } + } + } + + if (create) { + *r_bcd_first = bcd_first; + } +} + +/** + * \note There is no room for data going out of sync here. + * The layers and the states are stored together so this can be kept working. + */ +static void um_arraystore_cd_expand( + const BArrayCustomData *bcd, struct CustomData *cdata, const size_t data_len) +{ + CustomDataLayer *layer = cdata->layers; + while (bcd) { + const int stride = CustomData_sizeof(bcd->type); + for (int i = 0; i < bcd->states_len; i++) { + BLI_assert(bcd->type == layer->type); + if (bcd->states[i]) { + size_t state_len; + layer->data = BLI_array_store_state_data_get_alloc(bcd->states[i], &state_len); + BLI_assert(stride * data_len == state_len); + UNUSED_VARS_NDEBUG(stride, data_len); + } + else { + layer->data = NULL; + } + layer++; + } + bcd = bcd->next; + } +} + +static void um_arraystore_cd_free(BArrayCustomData *bcd) +{ + while (bcd) { + BArrayCustomData *bcd_next = bcd->next; + const int stride = CustomData_sizeof(bcd->type); + BArrayStore *bs = array_store_at_size_get(stride); + for (int i = 0; i < bcd->states_len; i++) { + if (bcd->states[i]) { + BLI_array_store_state_remove(bs, bcd->states[i]); + } + } + MEM_freeN(bcd); + bcd = bcd_next; + } +} + +/** + * \param create: When false, only free the arrays. + * This is done since when reading from an undo state, they must be temporarily expanded. + * then discarded afterwards, having this argument avoids having 2x code paths. + */ +static void um_arraystore_compact_ex( + UndoMesh *um, const UndoMesh *um_ref, + bool create) +{ + Mesh *me = &um->me; + + um_arraystore_cd_compact(&me->vdata, me->totvert, create, um_ref ? um_ref->store.vdata : NULL, &um->store.vdata); + um_arraystore_cd_compact(&me->edata, me->totedge, create, um_ref ? um_ref->store.edata : NULL, &um->store.edata); + um_arraystore_cd_compact(&me->ldata, me->totloop, create, um_ref ? um_ref->store.ldata : NULL, &um->store.ldata); + um_arraystore_cd_compact(&me->pdata, me->totpoly, create, um_ref ? um_ref->store.pdata : NULL, &um->store.pdata); + + if (me->key && me->key->totkey) { + const size_t stride = me->key->elemsize; + BArrayStore *bs = create ? array_store_at_size_ensure(stride) : NULL; + if (create) { + um->store.keyblocks = MEM_mallocN(me->key->totkey * sizeof(*um->store.keyblocks), __func__); + } + KeyBlock *keyblock = me->key->block.first; + for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) { + if (create) { + BArrayState *state_reference = + (um_ref && um_ref->me.key && (i < um_ref->me.key->totkey)) ? + um_ref->store.keyblocks[i] : NULL; + um->store.keyblocks[i] = BLI_array_store_state_add( + bs, keyblock->data, (size_t)keyblock->totelem * stride, + state_reference); + } + + if (keyblock->data) { + MEM_freeN(keyblock->data); + keyblock->data = NULL; + } + } + } + + if (me->mselect && me->totselect) { + BLI_assert(create == (um->store.mselect == NULL)); + if (create) { + BArrayState *state_reference = um_ref ? um_ref->store.mselect : NULL; + const size_t stride = sizeof(*me->mselect); + BArrayStore *bs = array_store_at_size_ensure(stride); + um->store.mselect = BLI_array_store_state_add( + bs, me->mselect, (size_t)me->totselect * stride, state_reference); + } + + /* keep me->totselect for validation */ + MEM_freeN(me->mselect); + me->mselect = NULL; + } + + if (create) { + um_arraystore.users += 1; + } + + BKE_mesh_update_customdata_pointers(me, false); +} + +/** + * Move data from allocated arrays to de-duplicated states and clear arrays. + */ +static void um_arraystore_compact(UndoMesh *um, const UndoMesh *um_ref) +{ + um_arraystore_compact_ex(um, um_ref, true); +} + +static void um_arraystore_compact_with_info(UndoMesh *um, const UndoMesh *um_ref) +{ +#ifdef DEBUG_PRINT + size_t size_expanded_prev, size_compacted_prev; + um_arraystore_memory_usage(&size_expanded_prev, &size_compacted_prev); +#endif + +#ifdef DEBUG_TIME + TIMEIT_START(mesh_undo_compact); +#endif + + um_arraystore_compact(um, um_ref); + +#ifdef DEBUG_TIME + TIMEIT_END(mesh_undo_compact); +#endif + +#ifdef DEBUG_PRINT + { + size_t size_expanded, size_compacted; + um_arraystore_memory_usage(&size_expanded, &size_compacted); + + const double percent_total = size_expanded ? + (((double)size_compacted / (double)size_expanded) * 100.0) : -1.0; + + size_t size_expanded_step = size_expanded - size_expanded_prev; + size_t size_compacted_step = size_compacted - size_compacted_prev; + const double percent_step = size_expanded_step ? + (((double)size_compacted_step / (double)size_expanded_step) * 100.0) : -1.0; + + printf("overall memory use: %.8f%% of expanded size\n", percent_total); + printf("step memory use: %.8f%% of expanded size\n", percent_step); + } +#endif +} + +#ifdef USE_ARRAY_STORE_THREAD + +struct UMArrayData { + UndoMesh *um; + const UndoMesh *um_ref; /* can be NULL */ +}; +static void um_arraystore_compact_cb(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + struct UMArrayData *um_data = taskdata; + um_arraystore_compact_with_info(um_data->um, um_data->um_ref); +} + +#endif /* USE_ARRAY_STORE_THREAD */ + +/** + * Remove data we only expanded for temporary use. + */ +static void um_arraystore_expand_clear(UndoMesh *um) +{ + um_arraystore_compact_ex(um, NULL, false); +} + +static void um_arraystore_expand(UndoMesh *um) +{ + Mesh *me = &um->me; + + um_arraystore_cd_expand(um->store.vdata, &me->vdata, me->totvert); + um_arraystore_cd_expand(um->store.edata, &me->edata, me->totedge); + um_arraystore_cd_expand(um->store.ldata, &me->ldata, me->totloop); + um_arraystore_cd_expand(um->store.pdata, &me->pdata, me->totpoly); + + if (um->store.keyblocks) { + const size_t stride = me->key->elemsize; + KeyBlock *keyblock = me->key->block.first; + for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) { + BArrayState *state = um->store.keyblocks[i]; + size_t state_len; + keyblock->data = BLI_array_store_state_data_get_alloc(state, &state_len); + BLI_assert(keyblock->totelem == (state_len / stride)); + UNUSED_VARS_NDEBUG(stride); + } + } + + if (um->store.mselect) { + const size_t stride = sizeof(*me->mselect); + BArrayState *state = um->store.mselect; + size_t state_len; + me->mselect = BLI_array_store_state_data_get_alloc(state, &state_len); + BLI_assert(me->totselect == (state_len / stride)); + UNUSED_VARS_NDEBUG(stride); + } + + /* not essential, but prevents accidental dangling pointer access */ + BKE_mesh_update_customdata_pointers(me, false); +} + +static void um_arraystore_free(UndoMesh *um) +{ + Mesh *me = &um->me; + + um_arraystore_cd_free(um->store.vdata); + um_arraystore_cd_free(um->store.edata); + um_arraystore_cd_free(um->store.ldata); + um_arraystore_cd_free(um->store.pdata); + + if (um->store.keyblocks) { + const size_t stride = me->key->elemsize; + BArrayStore *bs = array_store_at_size_get(stride); + for (int i = 0; i < me->key->totkey; i++) { + BArrayState *state = um->store.keyblocks[i]; + BLI_array_store_state_remove(bs, state); + } + MEM_freeN(um->store.keyblocks); + um->store.keyblocks = NULL; + } + + if (um->store.mselect) { + const size_t stride = sizeof(*me->mselect); + BArrayStore *bs = array_store_at_size_get(stride); + BArrayState *state = um->store.mselect; + BLI_array_store_state_remove(bs, state); + um->store.mselect = NULL; + } + + um_arraystore.users -= 1; + + BLI_assert(um_arraystore.users >= 0); + + if (um_arraystore.users == 0) { +#ifdef DEBUG_PRINT + printf("mesh undo store: freeing all data!\n"); +#endif + for (int i = 0; i < um_arraystore.bs_all_len; i += 1) { + if (um_arraystore.bs_all[i]) { + BLI_array_store_destroy(um_arraystore.bs_all[i]); + } + } + + MEM_freeN(um_arraystore.bs_all); + um_arraystore.bs_all = NULL; + um_arraystore.bs_all_len = 0; + +#ifdef USE_ARRAY_STORE_THREAD + BLI_task_pool_free(um_arraystore.task_pool); + um_arraystore.task_pool = NULL; +#endif + } + +} + +/** \} */ + +#endif /* USE_ARRAY_STORE */ + + +/* for callbacks */ +/* undo simply makes copies of a bmesh */ +static void *editbtMesh_to_undoMesh(void *emv, void *obdata) +{ + +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + if (um_arraystore.task_pool) { + BLI_task_pool_work_and_wait(um_arraystore.task_pool); + } +#endif + + BMEditMesh *em = emv; + Mesh *obme = obdata; + + UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh"); + + /* make sure shape keys work */ + um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL; + + /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ + + BM_mesh_bm_to_me( + em->bm, &um->me, (&(struct BMeshToMeshParams){ + .cd_mask_extra = CD_MASK_SHAPE_KEYINDEX, + })); + + um->selectmode = em->selectmode; + um->shapenr = em->bm->shapenr; + +#ifdef USE_ARRAY_STORE + { + /* We could be more clever here, + * the previous undo state may be from a separate mesh. */ + const UndoMesh *um_ref = um_arraystore.local_links.last ? + ((LinkData *)um_arraystore.local_links.last)->data : NULL; + + /* add oursrlves */ + BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um)); + +#ifdef USE_ARRAY_STORE_THREAD + if (um_arraystore.task_pool == NULL) { + TaskScheduler *scheduler = BLI_task_scheduler_get(); + um_arraystore.task_pool = BLI_task_pool_create_background(scheduler, NULL); + } + + struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__); + um_data->um = um; + um_data->um_ref = um_ref; + + BLI_task_pool_push( + um_arraystore.task_pool, + um_arraystore_compact_cb, um_data, true, TASK_PRIORITY_LOW); +#else + um_arraystore_compact_with_info(um, um_ref); +#endif + } +#endif + + return um; +} + +static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata) +{ + BMEditMesh *em = em_v, *em_tmp; + Object *ob = em->ob; + UndoMesh *um = um_v; + BMesh *bm; + Key *key = ((Mesh *) obdata)->key; + +#ifdef USE_ARRAY_STORE +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + BLI_task_pool_work_and_wait(um_arraystore.task_pool); +#endif + +#ifdef DEBUG_TIME + TIMEIT_START(mesh_undo_expand); +#endif + + um_arraystore_expand(um); + +#ifdef DEBUG_TIME + TIMEIT_END(mesh_undo_expand); +#endif +#endif /* USE_ARRAY_STORE */ + + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&um->me); + + em->bm->shapenr = um->shapenr; + + EDBM_mesh_free(em); + + bm = BM_mesh_create(&allocsize); + + BM_mesh_bm_from_me( + bm, &um->me, (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, .active_shapekey = um->shapenr, + })); + + em_tmp = BKE_editmesh_create(bm, true); + *em = *em_tmp; + + em->selectmode = um->selectmode; + bm->selectmode = um->selectmode; + em->ob = ob; + + /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens + * if the active is a basis for any other. */ + if (key && (key->type == KEY_RELATIVE)) { + /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume + * shapenr from restored bmesh and keyblock indices are in sync. */ + const int kb_act_idx = ob->shapenr - 1; + + /* If it is, let's patch the current mesh key block to its restored value. + * Else, the offsets won't be computed and it won't matter. */ + if (BKE_keyblock_is_basis(key, kb_act_idx)) { + KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx); + + if (kb_act->totelem != um->me.totvert) { + /* The current mesh has some extra/missing verts compared to the undo, adjust. */ + MEM_SAFE_FREE(kb_act->data); + kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__); + kb_act->totelem = um->me.totvert; + } + + BKE_keyblock_update_from_mesh(&um->me, kb_act); + } + } + + ob->shapenr = um->shapenr; + + MEM_freeN(em_tmp); + +#ifdef USE_ARRAY_STORE + um_arraystore_expand_clear(um); +#endif +} + +static void free_undo(void *um_v) +{ + UndoMesh *um = um_v; + Mesh *me = &um->me; + +#ifdef USE_ARRAY_STORE + +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + BLI_task_pool_work_and_wait(um_arraystore.task_pool); +#endif + + /* we need to expand so any allocations in custom-data are freed with the mesh */ + um_arraystore_expand(um); + + { + LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data)); + BLI_remlink(&um_arraystore.local_links, link); + MEM_freeN(link); + } + um_arraystore_free(um); +#endif + + if (me->key) { + BKE_key_free(me->key); + MEM_freeN(me->key); + } + + BKE_mesh_free(me); + MEM_freeN(me); +} + +static void *getEditMesh(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_MESH) { + Mesh *me = obedit->data; + return me->edit_btmesh; + } + return NULL; +} + +/* and this is all the undo system needs to know */ +void undo_push_mesh(bContext *C, const char *name) +{ + /* em->ob gets out of date and crashes on mesh undo, + * this is an easy way to ensure its OK + * though we could investigate the matter further. */ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + em->ob = obedit; + + undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL); +} diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index cac9e0afc7a..99be37845ee 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -45,7 +45,6 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_depsgraph.h" -#include "BKE_key.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" @@ -60,7 +59,6 @@ #include "ED_mesh.h" #include "ED_screen.h" -#include "ED_util.h" #include "ED_view3d.h" #include "mesh_intern.h" /* own include */ @@ -491,140 +489,6 @@ void EDBM_flag_enable_all(BMEditMesh *em, const char hflag) BM_mesh_elem_hflag_enable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, hflag, true); } -/**************-------------- Undo ------------*****************/ - -/* for callbacks */ - -static void *getEditMesh(bContext *C) -{ - Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_MESH) { - Mesh *me = obedit->data; - return me->edit_btmesh; - } - return NULL; -} - -typedef struct UndoMesh { - Mesh me; - int selectmode; - - /** \note - * this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]), - * but editing shape keys, going into object mode, removing or changing their order, - * then go back into editmode and undo will give issues - where the old index will be out of sync - * with the new object index. - * - * There are a few ways this could be made to work but for now its a known limitation with mixing - * object and editmode operations - Campbell */ - int shapenr; -} UndoMesh; - -/* undo simply makes copies of a bmesh */ -static void *editbtMesh_to_undoMesh(void *emv, void *obdata) -{ - BMEditMesh *em = emv; - Mesh *obme = obdata; - - UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh"); - - /* make sure shape keys work */ - um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL; - - /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ - - BM_mesh_bm_to_me( - em->bm, &um->me, (&(struct BMeshToMeshParams){ - .cd_mask_extra = CD_MASK_SHAPE_KEYINDEX, - })); - - um->selectmode = em->selectmode; - um->shapenr = em->bm->shapenr; - - return um; -} - -static void undoMesh_to_editbtMesh(void *umv, void *em_v, void *obdata) -{ - BMEditMesh *em = em_v, *em_tmp; - Object *ob = em->ob; - UndoMesh *um = umv; - BMesh *bm; - Key *key = ((Mesh *) obdata)->key; - - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&um->me); - - em->bm->shapenr = um->shapenr; - - EDBM_mesh_free(em); - - bm = BM_mesh_create(&allocsize); - - BM_mesh_bm_from_me( - bm, &um->me, (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, .active_shapekey = um->shapenr, - })); - - em_tmp = BKE_editmesh_create(bm, true); - *em = *em_tmp; - - em->selectmode = um->selectmode; - bm->selectmode = um->selectmode; - em->ob = ob; - - /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens - * if the active is a basis for any other. */ - if (key && (key->type == KEY_RELATIVE)) { - /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume - * shapenr from restored bmesh and keyblock indices are in sync. */ - const int kb_act_idx = ob->shapenr - 1; - - /* If it is, let's patch the current mesh key block to its restored value. - * Else, the offsets won't be computed and it won't matter. */ - if (BKE_keyblock_is_basis(key, kb_act_idx)) { - KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx); - - if (kb_act->totelem != um->me.totvert) { - /* The current mesh has some extra/missing verts compared to the undo, adjust. */ - MEM_SAFE_FREE(kb_act->data); - kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__); - kb_act->totelem = um->me.totvert; - } - - BKE_keyblock_update_from_mesh(&um->me, kb_act); - } - } - - ob->shapenr = um->shapenr; - - MEM_freeN(em_tmp); -} - -static void free_undo(void *me_v) -{ - Mesh *me = me_v; - if (me->key) { - BKE_key_free(me->key); - MEM_freeN(me->key); - } - - BKE_mesh_free(me); - MEM_freeN(me); -} - -/* and this is all the undo system needs to know */ -void undo_push_mesh(bContext *C, const char *name) -{ - /* em->ob gets out of date and crashes on mesh undo, - * this is an easy way to ensure its OK - * though we could investigate the matter further. */ - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - em->ob = obedit; - - undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL); -} - /** * Return a new UVVertMap from the editmesh */ diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c index c15e49a8ed1..a6d120b8943 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.c @@ -575,7 +575,7 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) glFramebufferTexture2DEXT( GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + ofs->color->fb_attachment, GL_TEXTURE_2D_MULTISAMPLE, ofs->color->bindcode, 0); - status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER_EXT); + status = glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { goto finally; } @@ -583,11 +583,11 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) /* write into new single-sample buffer */ glGenFramebuffersEXT(1, &fbo_blit); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, fbo_blit); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo_blit); glFramebufferTexture2DEXT( - GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, + GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex_blit, 0); - status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER_EXT); + status = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { goto finally; } diff --git a/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl b/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl index e1cd7e0684c..6b6679b60df 100644 --- a/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl @@ -88,7 +88,7 @@ void main() discard; } else if (stipple_id == STIPPLE_CHECKER_8PX) { - int result = int(mod(int(gl_FragCoord.x)/8 + int(gl_FragCoord.y) / 8, 2)); + int result = int(mod(int(gl_FragCoord.x) / 8 + int(gl_FragCoord.y) / 8, 2)); if (result != 0) discard; } @@ -158,7 +158,7 @@ void main() /* diffuse light */ vec3 light_diffuse = gl_LightSource[i].diffuse.rgb; float diffuse_bsdf = max(dot(N, light_direction), 0.0); - L_diffuse += light_diffuse*diffuse_bsdf; + L_diffuse += light_diffuse * diffuse_bsdf; #ifndef NO_SPECULAR /* specular light */ @@ -166,7 +166,7 @@ void main() vec3 H = gl_LightSource[i].halfVector.xyz; float specular_bsdf = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess); - L_specular += light_specular*specular_bsdf; + L_specular += light_specular * specular_bsdf; #endif } #else @@ -174,7 +174,7 @@ void main() #ifndef NO_SPECULAR /* view vector computation, depends on orthographics or perspective */ - vec3 V = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(varying_position): vec3(0.0, 0.0, -1.0); + vec3 V = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(varying_position) : vec3(0.0, 0.0, -1.0); #endif for (int i = 0; i < NUM_SCENE_LIGHTS; i++) { @@ -205,14 +205,14 @@ void main() float distance = length(d); intensity /= gl_LightSource[i].constantAttenuation + - gl_LightSource[i].linearAttenuation * distance + - gl_LightSource[i].quadraticAttenuation * distance * distance; + gl_LightSource[i].linearAttenuation * distance + + gl_LightSource[i].quadraticAttenuation * distance * distance; } /* diffuse light */ vec3 light_diffuse = gl_LightSource[i].diffuse.rgb; float diffuse_bsdf = max(dot(N, light_direction), 0.0); - L_diffuse += light_diffuse*diffuse_bsdf*intensity; + L_diffuse += light_diffuse * diffuse_bsdf * intensity; #ifndef NO_SPECULAR /* specular light */ @@ -220,7 +220,7 @@ void main() vec3 H = normalize(light_direction - V); float specular_bsdf = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess); - L_specular += light_specular*specular_bsdf*intensity; + L_specular += light_specular * specular_bsdf * intensity; #endif } #endif @@ -250,7 +250,7 @@ void main() vec3 L = gl_FrontLightModelProduct.sceneColor.rgb + L_diffuse; #ifndef NO_SPECULAR - L += L_specular*gl_FrontMaterial.specular.rgb; + L += L_specular * gl_FrontMaterial.specular.rgb; #endif /* write out fragment color */ diff --git a/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl b/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl index 674dd534352..a88681a5fd3 100644 --- a/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl +++ b/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl @@ -1,6 +1,6 @@ /* -* Used the implementation of wide lines of Timo Suoranta (http://neure.dy.fi/wideline.html) -*/ + * Used the implementation of wide lines of Timo Suoranta (http://neure.dy.fi/wideline.html) + */ #define PASSTHROUGH 0 @@ -24,72 +24,72 @@ uniform int stipple_factor; void main(void) { - vec2 window_size = viewport.zw; - vec4 start = gl_in[0].gl_Position; - vec4 end = gl_in[1].gl_Position; + vec2 window_size = viewport.zw; + vec4 start = gl_in[0].gl_Position; + vec4 end = gl_in[1].gl_Position; #if PASSTHROUGH - gl_Position = start; EmitVertex(); - gl_Position = end; EmitVertex(); - EndPrimitive(); - return; + gl_Position = start; EmitVertex(); + gl_Position = end; EmitVertex(); + EndPrimitive(); + return; #endif -/* t = 0 t = ~(len(end - start) + 2*line_width) - * A-------------------------------------B - * | | | | - * | side | | - * | | | | - * |--axis--*start--------------*end-----| - * | | | | - * | | | | - * | | | | - * D-------------------------------------C - */ - - /* Clip the line before homogenization. - * Compute line start and end distances to nearplane in clipspace - * Distances are t0 = dot(start, plane) and t1 = dot(end, plane) - */ - float t0 = start.z + start.w; - float t1 = end.z + end.w; - if (t0 < 0.0) { - if (t1 < 0.0) { - return; - } - start = mix(start, end, (0 - t0) / (t1 - t0)); - } - if (t1 < 0.0) { - end = mix(start, end, (0 - t0) / (t1 - t0)); - } - - /* Compute line axis and side vector in screen space */ - vec2 startInNDC = start.xy / start.w; /* clip to NDC: homogenize and drop z */ - vec2 endInNDC = end.xy / end.w; - vec2 lineInNDC = endInNDC - startInNDC; - vec2 lineInScreen = lineInNDC * window_size; /* ndc to screen (direction vector) */ - - vec2 axisInScreen = normalize(lineInScreen); - vec2 sideInScreen = vec2(-axisInScreen.y, axisInScreen.x); /* rotate */ - vec2 axisInNDC = axisInScreen / window_size; /* screen to NDC */ - vec2 sideInNDC = sideInScreen / window_size; - vec4 axis = vec4(axisInNDC, 0.0, 0.0) * line_width; /* NDC to clip (delta vector) */ - vec4 side = vec4(sideInNDC, 0.0, 0.0) * line_width; - - vec4 A = (start + (side - axis) * start.w); - vec4 B = (end + (side + axis) * end.w); - vec4 C = (end - (side - axis) * end.w); - vec4 D = (start - (side + axis) * start.w); - - /* There is no relation between lines yet */ - /* TODO Pass here t0 to make continuous pattern. */ - t0 = 0; - t1 = (length(lineInScreen) + 2*line_width)/ (2*line_width * stipple_factor); - - gl_Position = A; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); - gl_Position = D; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); - gl_Position = B; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); - gl_Position = C; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); - EndPrimitive(); + /* t = 0 t = ~(len(end - start) + 2*line_width) + * A-------------------------------------B + * | | | | + * | side | | + * | | | | + * |--axis--*start--------------*end-----| + * | | | | + * | | | | + * | | | | + * D-------------------------------------C + */ + + /* Clip the line before homogenization. + * Compute line start and end distances to nearplane in clipspace + * Distances are t0 = dot(start, plane) and t1 = dot(end, plane) + */ + float t0 = start.z + start.w; + float t1 = end.z + end.w; + if (t0 < 0.0) { + if (t1 < 0.0) { + return; + } + start = mix(start, end, (0 - t0) / (t1 - t0)); + } + if (t1 < 0.0) { + end = mix(start, end, (0 - t0) / (t1 - t0)); + } + + /* Compute line axis and side vector in screen space */ + vec2 startInNDC = start.xy / start.w; /* clip to NDC: homogenize and drop z */ + vec2 endInNDC = end.xy / end.w; + vec2 lineInNDC = endInNDC - startInNDC; + vec2 lineInScreen = lineInNDC * window_size; /* ndc to screen (direction vector) */ + + vec2 axisInScreen = normalize(lineInScreen); + vec2 sideInScreen = vec2(-axisInScreen.y, axisInScreen.x); /* rotate */ + vec2 axisInNDC = axisInScreen / window_size; /* screen to NDC */ + vec2 sideInNDC = sideInScreen / window_size; + vec4 axis = vec4(axisInNDC, 0.0, 0.0) * line_width; /* NDC to clip (delta vector) */ + vec4 side = vec4(sideInNDC, 0.0, 0.0) * line_width; + + vec4 A = (start + (side - axis) * start.w); + vec4 B = (end + (side + axis) * end.w); + vec4 C = (end - (side - axis) * end.w); + vec4 D = (start - (side + axis) * start.w); + + /* There is no relation between lines yet */ + /* TODO Pass here t0 to make continuous pattern. */ + t0 = 0; + t1 = (length(lineInScreen) + 2 * line_width) / (2 * line_width * stipple_factor); + + gl_Position = A; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); + gl_Position = D; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); + gl_Position = B; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); + gl_Position = C; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); + EndPrimitive(); } #else diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl index e9dab04de5d..338ef6d51a7 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl @@ -115,9 +115,9 @@ void second_pass() color += texture2D(colorbuffer, uvcoordsvar.xy + invrendertargetdim) * 0.234375; color += texture2D(colorbuffer, uvcoordsvar.xy + 2.5 * invrendertargetdim) * 0.09375; color += texture2D(colorbuffer, uvcoordsvar.xy + 4.5 * invrendertargetdim) * 0.015625; - color += texture2D(colorbuffer, uvcoordsvar.xy -invrendertargetdim) * 0.234375; - color += texture2D(colorbuffer, uvcoordsvar.xy -2.5 * invrendertargetdim) * 0.09375; - color += texture2D(colorbuffer, uvcoordsvar.xy -4.5 * invrendertargetdim) * 0.015625; + color += texture2D(colorbuffer, uvcoordsvar.xy - invrendertargetdim) * 0.234375; + color += texture2D(colorbuffer, uvcoordsvar.xy - 2.5 * invrendertargetdim) * 0.09375; + color += texture2D(colorbuffer, uvcoordsvar.xy - 4.5 * invrendertargetdim) * 0.015625; gl_FragColor = color; } @@ -128,7 +128,7 @@ void third_pass() { vec4 color = texture2D(colorbuffer, uvcoordsvar.xy); vec4 color_blurred = texture2D(blurredcolorbuffer, uvcoordsvar.xy); - float coc = 2.0 * max(color_blurred.a, color.a); - color.a; + float coc = 2.0 * max(color_blurred.a, color.a); -color.a; gl_FragColor = vec4(color.rgb, coc); } @@ -146,7 +146,7 @@ void fourth_pass() vec4 small_sample_blur(in sampler2D colorbuffer, in vec2 uv, in vec4 color) { - float weight = 1.0/ 17.0; + float weight = 1.0 / 17.0; vec4 result = weight * color; weight *= 4.0; diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl index 7657fbb71ab..182113367d3 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl @@ -103,7 +103,7 @@ void accumulate_pass(void) { r = 1.0; else r = cos(M_PI / dof_params.w) / - (cos(theta - (2.0 * M_PI / dof_params.w) * floor((dof_params.w * theta + M_PI) / (2.0 * M_PI)))); + (cos(theta - (2.0 * M_PI / dof_params.w) * floor((dof_params.w * theta + M_PI) / (2.0 * M_PI)))); if (dot(particlecoord, particlecoord) > r * r) discard; diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl index a2ef990c4e8..63b57d5775c 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl @@ -38,8 +38,12 @@ void vert_dof_first_pass() void vert_dof_fourth_pass() { vec4 halfpixel = vec4(-0.5, 0.5, -0.5, 0.5); - uvcoordsvar = gl_MultiTexCoord0.xxyy + halfpixel * vec4(invrendertargetdim.x, - invrendertargetdim.x, invrendertargetdim.y, invrendertargetdim.y); + uvcoordsvar = gl_MultiTexCoord0.xxyy + + halfpixel * + vec4(invrendertargetdim.x, + invrendertargetdim.x, + invrendertargetdim.y, + invrendertargetdim.y); gl_Position = gl_Vertex; } diff --git a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl index c2cd927898e..054a2f795ee 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl @@ -77,7 +77,7 @@ float calculate_ssao_factor(float depth) /* use minor bias here to avoid self shadowing */ if (f > 0.05 * len + 0.0001) - factor += f * 1.0/(len * (1.0 + len * len * ssao_params.z)); + factor += f * 1.0 / (len * (1.0 + len * len * ssao_params.z)); } } diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl index 16fba0dd055..1663915549c 100644 --- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl +++ b/source/blender/gpu/shaders/gpu_shader_geometry.glsl @@ -49,7 +49,7 @@ void emit_flat(int index, vec3 normal) varposition = outpt.v.position.xyz; /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1)); + vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); vec2 st = quadst[index]; INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); @@ -70,7 +70,7 @@ void emit_smooth(int index) varposition = outpt.v.position.xyz; /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1)); + vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); vec2 st = quadst[index]; INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index a63e7b8dbf0..91ca6a51bbb 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -3,7 +3,7 @@ float convert_rgba_to_float(vec4 color) { #ifdef USE_NEW_SHADING - return color.r*0.2126 + color.g*0.7152 + color.b*0.0722; + return color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; #else return (color.r + color.g + color.b) / 3.0; #endif @@ -39,11 +39,11 @@ void rgb_to_hsv(vec4 rgb, out vec4 outcol) cmax = max(rgb[0], max(rgb[1], rgb[2])); cmin = min(rgb[0], min(rgb[1], rgb[2])); - cdelta = cmax-cmin; + cdelta = cmax - cmin; v = cmax; - if (cmax!=0.0) - s = cdelta/cmax; + if (cmax != 0.0) + s = cdelta / cmax; else { s = 0.0; h = 0.0; @@ -53,15 +53,15 @@ void rgb_to_hsv(vec4 rgb, out vec4 outcol) h = 0.0; } else { - c = (vec3(cmax, cmax, cmax) - rgb.xyz)/cdelta; + c = (vec3(cmax, cmax, cmax) - rgb.xyz) / cdelta; - if (rgb.x==cmax) h = c[2] - c[1]; - else if (rgb.y==cmax) h = 2.0 + c[0] - c[2]; + if (rgb.x == cmax) h = c[2] - c[1]; + else if (rgb.y == cmax) h = 2.0 + c[0] - c[2]; else h = 4.0 + c[1] - c[0]; h /= 6.0; - if (h<0.0) + if (h < 0.0) h += 1.0; } @@ -77,21 +77,21 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol) s = hsv[1]; v = hsv[2]; - if (s==0.0) { + if (s == 0.0) { rgb = vec3(v, v, v); } else { - if (h==1.0) + if (h == 1.0) h = 0.0; - + h *= 6.0; i = floor(h); f = h - i; rgb = vec3(f, f, f); - p = v*(1.0-s); - q = v*(1.0-(s*f)); - t = v*(1.0-(s*(1.0-f))); - + p = v * (1.0 - s); + q = v * (1.0 - (s * f)); + t = v * (1.0 - (s * (1.0 - f))); + if (i == 0.0) rgb = vec3(v, t, p); else if (i == 1.0) rgb = vec3(q, v, p); else if (i == 2.0) rgb = vec3(p, v, t); @@ -106,17 +106,17 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol) float srgb_to_linearrgb(float c) { if (c < 0.04045) - return (c < 0.0) ? 0.0: c * (1.0 / 12.92); + return (c < 0.0) ? 0.0 : c * (1.0 / 12.92); else - return pow((c + 0.055)*(1.0/1.055), 2.4); + return pow((c + 0.055) * (1.0 / 1.055), 2.4); } float linearrgb_to_srgb(float c) { if (c < 0.0031308) - return (c < 0.0) ? 0.0: c * 12.92; + return (c < 0.0) ? 0.0 : c * 12.92; else - return 1.055 * pow(c, 1.0/2.4) - 0.055; + return 1.055 * pow(c, 1.0 / 2.4) - 0.055; } void srgb_to_linearrgb(vec4 col_from, out vec4 col_to) @@ -168,7 +168,7 @@ void vcol_attribute(vec4 attvcol, out vec4 vcol) void uv_attribute(vec2 attuv, out vec3 uv) { - uv = vec3(attuv*2.0 - vec2(1.0, 1.0), 0.0); + uv = vec3(attuv * 2.0 - vec2(1.0, 1.0), 0.0); } void geom( @@ -177,15 +177,15 @@ void geom( out vec3 normal, out vec4 vcol, out float vcol_alpha, out float frontback) { local = co; - view = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(local): vec3(0.0, 0.0, -1.0); - global = (viewinvmat*vec4(local, 1.0)).xyz; + view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(local) : vec3(0.0, 0.0, -1.0); + global = (viewinvmat * vec4(local, 1.0)).xyz; orco = attorco; uv_attribute(attuv, uv); - normal = -normalize(nor); /* blender render normal is negated */ + normal = -normalize(nor); /* blender render normal is negated */ vcol_attribute(attvcol, vcol); srgb_to_linearrgb(vcol, vcol); vcol_alpha = attvcol.a; - frontback = (gl_FrontFacing)? 1.0: 0.0; + frontback = (gl_FrontFacing) ? 1.0 : 0.0; } void particle_info( @@ -210,12 +210,48 @@ void vect_normalize(vec3 vin, out vec3 vout) void direction_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) { - vout = (mat*vec4(vin, 0.0)).xyz; + vout = (mat * vec4(vin, 0.0)).xyz; } void point_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) { - vout = (mat*vec4(vin, 1.0)).xyz; + vout = (mat * vec4(vin, 1.0)).xyz; +} + +void point_texco_remap_square(vec3 vin, out vec3 vout) +{ + vout = vec3(vin - vec3(0.5, 0.5, 0.5)) * 2.0; +} + +void point_map_to_sphere(vec3 vin, out vec3 vout) +{ + float len = length(vin); + float v, u; + if (len > 0.0) { + if (vin.x == 0.0 && vin.y == 0.0) + u = 0.0; + else + u = (1.0 - atan(vin.x, vin.y) / M_PI) / 2.0; + + v = 1.0 - acos(vin.z / len) / M_PI; + } + else + v = u = 0.0; + + vout = vec3(u, v, 0.0); +} + +void point_map_to_tube(vec3 vin, out vec3 vout) +{ + float u, v; + v = (vin.z + 1.0) * 0.5; + float len = sqrt(vin.x * vin.x + vin.y * vin[1]); + if (len > 0.0) + u = (1.0 - (atan(vin.x / len, vin.y / len) / M_PI)) * 0.5; + else + v = u = 0.0; + + vout = vec3(u, v, 0.0); } void mapping(vec3 vec, mat4 mat, vec3 minvec, vec3 maxvec, float domin, float domax, out vec3 outvec) @@ -322,9 +358,9 @@ void math_pow(float val1, float val2, out float outval) void math_log(float val1, float val2, out float outval) { if (val1 > 0.0 && val2 > 0.0) - outval= log2(val1) / log2(val2); + outval = log2(val1) / log2(val2); else - outval= 0.0; + outval = 0.0; } void math_max(float val1, float val2, out float outval) @@ -339,7 +375,7 @@ void math_min(float val1, float val2, out float outval) void math_round(float val, out float outval) { - outval= floor(val + 0.5); + outval = floor(val + 0.5); } void math_less_than(float val1, float val2, out float outval) @@ -377,19 +413,19 @@ void math_abs(float val1, out float outval) void squeeze(float val, float width, float center, out float outval) { - outval = 1.0/(1.0 + pow(2.71828183, -((val-center)*width))); + outval = 1.0 / (1.0 + pow(2.71828183, -((val - center) * width))); } void vec_math_add(vec3 v1, vec3 v2, out vec3 outvec, out float outval) { outvec = v1 + v2; - outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2]))/3.0; + outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) / 3.0; } void vec_math_sub(vec3 v1, vec3 v2, out vec3 outvec, out float outval) { outvec = v1 - v2; - outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2]))/3.0; + outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) / 3.0; } void vec_math_average(vec3 v1, vec3 v2, out vec3 outvec, out float outval) @@ -400,7 +436,7 @@ void vec_math_average(vec3 v1, vec3 v2, out vec3 outvec, out float outval) } void vec_math_mix(float strength, vec3 v1, vec3 v2, out vec3 outvec) { - outvec = strength*v1 + (1 - strength) * v2; + outvec = strength * v1 + (1 - strength) * v2; } void vec_math_dot(vec3 v1, vec3 v2, out vec3 outvec, out float outval) @@ -447,12 +483,12 @@ void normal_new_shading(vec3 dir, vec3 nor, out vec3 outnor, out float outdot) void curves_vec(float fac, vec3 vec, sampler2D curvemap, out vec3 outvec) { - outvec.x = texture2D(curvemap, vec2((vec.x + 1.0)*0.5, 0.0)).x; - outvec.y = texture2D(curvemap, vec2((vec.y + 1.0)*0.5, 0.0)).y; - outvec.z = texture2D(curvemap, vec2((vec.z + 1.0)*0.5, 0.0)).z; + outvec.x = texture2D(curvemap, vec2((vec.x + 1.0) * 0.5, 0.0)).x; + outvec.y = texture2D(curvemap, vec2((vec.y + 1.0) * 0.5, 0.0)).y; + outvec.z = texture2D(curvemap, vec2((vec.z + 1.0) * 0.5, 0.0)).z; if (fac != 1.0) - outvec = (outvec*fac) + (vec*(1.0-fac)); + outvec = (outvec * fac) + (vec * (1.0 - fac)); } @@ -463,7 +499,7 @@ void curves_rgb(float fac, vec4 col, sampler2D curvemap, out vec4 outcol) outcol.b = texture2D(curvemap, vec2(texture2D(curvemap, vec2(col.b, 0.0)).a, 0.0)).b; if (fac != 1.0) - outcol = (outcol*fac) + (col*(1.0-fac)); + outcol = (outcol * fac) + (col * (1.0 - fac)); outcol.a = col.a; } @@ -516,11 +552,11 @@ void set_rgba_one(out vec4 outval) void brightness_contrast(vec4 col, float brightness, float contrast, out vec4 outcol) { float a = 1.0 + contrast; - float b = brightness - contrast*0.5; + float b = brightness - contrast * 0.5; - outcol.r = max(a*col.r + b, 0.0); - outcol.g = max(a*col.g + b, 0.0); - outcol.b = max(a*col.b + b, 0.0); + outcol.r = max(a * col.r + b, 0.0); + outcol.g = max(a * col.g + b, 0.0); + outcol.b = max(a * col.b + b, 0.0); outcol.a = col.a; } @@ -550,7 +586,7 @@ void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol) fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; - outcol = vec4(1.0) - (vec4(facm) + fac*(vec4(1.0) - col2))*(vec4(1.0) - col1); + outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1); outcol.a = col1.a; } @@ -562,19 +598,19 @@ void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol = col1; if (outcol.r < 0.5) - outcol.r *= facm + 2.0*fac*col2.r; + outcol.r *= facm + 2.0 * fac * col2.r; else - outcol.r = 1.0 - (facm + 2.0*fac*(1.0 - col2.r))*(1.0 - outcol.r); + outcol.r = 1.0 - (facm + 2.0 * fac * (1.0 - col2.r)) * (1.0 - outcol.r); if (outcol.g < 0.5) - outcol.g *= facm + 2.0*fac*col2.g; + outcol.g *= facm + 2.0 * fac * col2.g; else - outcol.g = 1.0 - (facm + 2.0*fac*(1.0 - col2.g))*(1.0 - outcol.g); + outcol.g = 1.0 - (facm + 2.0 * fac * (1.0 - col2.g)) * (1.0 - outcol.g); if (outcol.b < 0.5) - outcol.b *= facm + 2.0*fac*col2.b; + outcol.b *= facm + 2.0 * fac * col2.b; else - outcol.b = 1.0 - (facm + 2.0*fac*(1.0 - col2.b))*(1.0 - outcol.b); + outcol.b = 1.0 - (facm + 2.0 * fac * (1.0 - col2.b)) * (1.0 - outcol.b); } void mix_sub(float fac, vec4 col1, vec4 col2, out vec4 outcol) @@ -591,9 +627,9 @@ void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol = col1; - if (col2.r != 0.0) outcol.r = facm*outcol.r + fac*outcol.r/col2.r; - if (col2.g != 0.0) outcol.g = facm*outcol.g + fac*outcol.g/col2.g; - if (col2.b != 0.0) outcol.b = facm*outcol.b + fac*outcol.b/col2.b; + if (col2.r != 0.0) outcol.r = facm * outcol.r + fac * outcol.r / col2.r; + if (col2.g != 0.0) outcol.g = facm * outcol.g + fac * outcol.g / col2.g; + if (col2.b != 0.0) outcol.b = facm * outcol.b + fac * outcol.b / col2.b; } void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol) @@ -606,14 +642,14 @@ void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol) { fac = clamp(fac, 0.0, 1.0); - outcol.rgb = min(col1.rgb, col2.rgb*fac); + outcol.rgb = min(col1.rgb, col2.rgb * fac); outcol.a = col1.a; } void mix_light(float fac, vec4 col1, vec4 col2, out vec4 outcol) { fac = clamp(fac, 0.0, 1.0); - outcol.rgb = max(col1.rgb, col2.rgb*fac); + outcol.rgb = max(col1.rgb, col2.rgb * fac); outcol.a = col1.a; } @@ -623,28 +659,28 @@ void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol = col1; if (outcol.r != 0.0) { - float tmp = 1.0 - fac*col2.r; + float tmp = 1.0 - fac * col2.r; if (tmp <= 0.0) outcol.r = 1.0; - else if ((tmp = outcol.r/tmp) > 1.0) + else if ((tmp = outcol.r / tmp) > 1.0) outcol.r = 1.0; else outcol.r = tmp; } if (outcol.g != 0.0) { - float tmp = 1.0 - fac*col2.g; + float tmp = 1.0 - fac * col2.g; if (tmp <= 0.0) outcol.g = 1.0; - else if ((tmp = outcol.g/tmp) > 1.0) + else if ((tmp = outcol.g / tmp) > 1.0) outcol.g = 1.0; else outcol.g = tmp; } if (outcol.b != 0.0) { - float tmp = 1.0 - fac*col2.b; + float tmp = 1.0 - fac * col2.b; if (tmp <= 0.0) outcol.b = 1.0; - else if ((tmp = outcol.b/tmp) > 1.0) + else if ((tmp = outcol.b / tmp) > 1.0) outcol.b = 1.0; else outcol.b = tmp; @@ -658,30 +694,30 @@ void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol = col1; - tmp = facm + fac*col2.r; + tmp = facm + fac * col2.r; if (tmp <= 0.0) outcol.r = 0.0; - else if ((tmp = (1.0 - (1.0 - outcol.r)/tmp)) < 0.0) + else if ((tmp = (1.0 - (1.0 - outcol.r) / tmp)) < 0.0) outcol.r = 0.0; else if (tmp > 1.0) outcol.r = 1.0; else outcol.r = tmp; - tmp = facm + fac*col2.g; + tmp = facm + fac * col2.g; if (tmp <= 0.0) outcol.g = 0.0; - else if ((tmp = (1.0 - (1.0 - outcol.g)/tmp)) < 0.0) + else if ((tmp = (1.0 - (1.0 - outcol.g) / tmp)) < 0.0) outcol.g = 0.0; else if (tmp > 1.0) outcol.g = 1.0; else outcol.g = tmp; - tmp = facm + fac*col2.b; + tmp = facm + fac * col2.b; if (tmp <= 0.0) outcol.b = 0.0; - else if ((tmp = (1.0 - (1.0 - outcol.b)/tmp)) < 0.0) + else if ((tmp = (1.0 - (1.0 - outcol.b) / tmp)) < 0.0) outcol.b = 0.0; else if (tmp > 1.0) outcol.b = 1.0; @@ -702,7 +738,7 @@ void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol) if (hsv2.y != 0.0) { rgb_to_hsv(outcol, hsv); hsv.x = hsv2.x; - hsv_to_rgb(hsv, tmp); + hsv_to_rgb(hsv, tmp); outcol = mix(outcol, tmp, fac); outcol.a = col1.a; @@ -722,7 +758,7 @@ void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol) if (hsv.y != 0.0) { rgb_to_hsv(col2, hsv2); - hsv.y = facm*hsv.y + fac*hsv2.y; + hsv.y = facm * hsv.y + fac * hsv2.y; hsv_to_rgb(hsv, outcol); } } @@ -736,7 +772,7 @@ void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol) rgb_to_hsv(col1, hsv); rgb_to_hsv(col2, hsv2); - hsv.z = facm*hsv.z + fac*hsv2.z; + hsv.z = facm * hsv.z + fac * hsv2.z; hsv_to_rgb(hsv, outcol); } @@ -754,7 +790,7 @@ void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol) rgb_to_hsv(outcol, hsv); hsv.x = hsv2.x; hsv.y = hsv2.y; - hsv_to_rgb(hsv, tmp); + hsv_to_rgb(hsv, tmp); outcol = mix(outcol, tmp, fac); outcol.a = col1.a; @@ -766,16 +802,16 @@ void mix_soft(float fac, vec4 col1, vec4 col2, out vec4 outcol) fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; - vec4 one= vec4(1.0); - vec4 scr= one - (one - col2)*(one - col1); - outcol = facm*col1 + fac*((one - col1)*col2*col1 + col1*scr); + vec4 one = vec4(1.0); + vec4 scr = one - (one - col2) * (one - col1); + outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr); } void mix_linear(float fac, vec4 col1, vec4 col2, out vec4 outcol) { fac = clamp(fac, 0.0, 1.0); - outcol = col1 + fac*(2.0*(col2 - vec4(0.5))); + outcol = col1 + fac * (2.0 * (col2 - vec4(0.5))); } void valtorgb(float fac, sampler2D colormap, out vec4 outcol, out float outalpha) @@ -784,12 +820,12 @@ void valtorgb(float fac, sampler2D colormap, out vec4 outcol, out float outalpha outalpha = outcol.a; } -void rgbtobw(vec4 color, out float outval) +void rgbtobw(vec4 color, out float outval) { #ifdef USE_NEW_SHADING - outval = color.r*0.2126 + color.g*0.7152 + color.b*0.0722; + outval = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; #else - outval = color.r*0.35 + color.g*0.45 + color.b*0.2; /* keep these factors in sync with texture.h:RGBTOBW */ + outval = color.r * 0.35 + color.g * 0.45 + color.b * 0.2; /* keep these factors in sync with texture.h:RGBTOBW */ #endif } @@ -816,11 +852,11 @@ void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 ou rgb_to_hsv(col, hsv); hsv[0] += (hue - 0.5); - if (hsv[0]>1.0) hsv[0]-=1.0; else if (hsv[0]<0.0) hsv[0]+= 1.0; + if (hsv[0] > 1.0) hsv[0] -= 1.0; else if (hsv[0] < 0.0) hsv[0] += 1.0; hsv[1] *= sat; - if (hsv[1]>1.0) hsv[1]= 1.0; else if (hsv[1]<0.0) hsv[1]= 0.0; + if (hsv[1] > 1.0) hsv[1] = 1.0; else if (hsv[1] < 0.0) hsv[1] = 0.0; hsv[2] *= value; - if (hsv[2]>1.0) hsv[2]= 1.0; else if (hsv[2]<0.0) hsv[2]= 0.0; + if (hsv[2] > 1.0) hsv[2] = 1.0; else if (hsv[2] < 0.0) hsv[2] = 0.0; hsv_to_rgb(hsv, outcol); @@ -880,19 +916,19 @@ void texture_flip_blend(vec3 vec, out vec3 outvec) void texture_blend_lin(vec3 vec, out float outval) { - outval = (1.0+vec.x)/2.0; + outval = (1.0 + vec.x) / 2.0; } void texture_blend_quad(vec3 vec, out float outval) { - outval = max((1.0+vec.x)/2.0, 0.0); + outval = max((1.0 + vec.x) / 2.0, 0.0); outval *= outval; } void texture_wood_sin(vec3 vec, out float value, out vec4 color, out vec3 normal) { - float a = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z)*20.0; - float wi = 0.5 + 0.5*sin(a); + float a = sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) * 20.0; + float wi = 0.5 + 0.5 * sin(a); value = wi; color = vec4(wi, wi, wi, 1.0); @@ -901,12 +937,12 @@ void texture_wood_sin(vec3 vec, out float value, out vec4 color, out vec3 normal void texture_image(vec3 vec, sampler2D ima, out float value, out vec4 color, out vec3 normal) { - color = texture2D(ima, (vec.xy + vec2(1.0, 1.0))*0.5); + color = texture2D(ima, (vec.xy + vec2(1.0, 1.0)) * 0.5); value = color.a; - normal.x = 2.0*(color.r - 0.5); - normal.y = 2.0*(0.5 - color.g); - normal.z = 2.0*(color.b - 0.5); + normal.x = 2.0 * (color.r - 0.5); + normal.y = 2.0 * (0.5 - color.g); + normal.z = 2.0 * (color.b - 0.5); } /************* MTEX *****************/ @@ -937,17 +973,17 @@ void texco_tangent(vec4 tangent, out vec3 outtangent) void texco_global(mat4 viewinvmat, vec3 co, out vec3 global) { - global = (viewinvmat*vec4(co, 1.0)).xyz; + global = (viewinvmat * vec4(co, 1.0)).xyz; } void texco_object(mat4 viewinvmat, mat4 obinvmat, vec3 co, out vec3 object) { - object = (obinvmat*(viewinvmat*vec4(co, 1.0))).xyz; + object = (obinvmat * (viewinvmat * vec4(co, 1.0))).xyz; } void texco_refl(vec3 vn, vec3 view, out vec3 ref) { - ref = view - 2.0*dot(vn, view)*vn; + ref = view - 2.0 * dot(vn, view) * vn; } void shade_norm(vec3 normal, out vec3 outnormal) @@ -966,9 +1002,9 @@ void mtex_rgb_blend(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 i float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = fact*texcol + facm*outcol; + incol = fact * texcol + facm * outcol; } void mtex_rgb_mul(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -976,9 +1012,9 @@ void mtex_rgb_mul(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = (facm + fact*texcol)*outcol; + incol = (facm + fact * texcol) * outcol; } void mtex_rgb_screen(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -986,9 +1022,9 @@ void mtex_rgb_screen(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = vec3(1.0) - (vec3(facm) + fact*(vec3(1.0) - texcol))*(vec3(1.0) - outcol); + incol = vec3(1.0) - (vec3(facm) + fact * (vec3(1.0) - texcol)) * (vec3(1.0) - outcol); } void mtex_rgb_overlay(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -996,32 +1032,32 @@ void mtex_rgb_overlay(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; if (outcol.r < 0.5) - incol.r = outcol.r*(facm + 2.0*fact*texcol.r); + incol.r = outcol.r * (facm + 2.0 * fact * texcol.r); else - incol.r = 1.0 - (facm + 2.0*fact*(1.0 - texcol.r))*(1.0 - outcol.r); + incol.r = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.r)) * (1.0 - outcol.r); if (outcol.g < 0.5) - incol.g = outcol.g*(facm + 2.0*fact*texcol.g); + incol.g = outcol.g * (facm + 2.0 * fact * texcol.g); else - incol.g = 1.0 - (facm + 2.0*fact*(1.0 - texcol.g))*(1.0 - outcol.g); + incol.g = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.g)) * (1.0 - outcol.g); if (outcol.b < 0.5) - incol.b = outcol.b*(facm + 2.0*fact*texcol.b); + incol.b = outcol.b * (facm + 2.0 * fact * texcol.b); else - incol.b = 1.0 - (facm + 2.0*fact*(1.0 - texcol.b))*(1.0 - outcol.b); + incol.b = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.b)) * (1.0 - outcol.b); } void mtex_rgb_sub(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) { - incol = -fact*facg*texcol + outcol; + incol = -fact * facg * texcol + outcol; } void mtex_rgb_add(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) { - incol = fact*facg*texcol + outcol; + incol = fact * facg * texcol + outcol; } void mtex_rgb_div(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -1029,11 +1065,11 @@ void mtex_rgb_div(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - if (texcol.r != 0.0) incol.r = facm*outcol.r + fact*outcol.r/texcol.r; - if (texcol.g != 0.0) incol.g = facm*outcol.g + fact*outcol.g/texcol.g; - if (texcol.b != 0.0) incol.b = facm*outcol.b + fact*outcol.b/texcol.b; + if (texcol.r != 0.0) incol.r = facm * outcol.r + fact * outcol.r / texcol.r; + if (texcol.g != 0.0) incol.g = facm * outcol.g + fact * outcol.g / texcol.g; + if (texcol.b != 0.0) incol.b = facm * outcol.b + fact * outcol.b / texcol.b; } void mtex_rgb_diff(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -1041,9 +1077,9 @@ void mtex_rgb_diff(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 in float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = facm*outcol + fact*abs(texcol - outcol); + incol = facm * outcol + fact * abs(texcol - outcol); } void mtex_rgb_dark(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -1051,7 +1087,7 @@ void mtex_rgb_dark(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 in float facm, col; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; incol.r = min(outcol.r, texcol.r) * fact + outcol.r * facm; incol.g = min(outcol.g, texcol.g) * fact + outcol.g * facm; @@ -1064,11 +1100,11 @@ void mtex_rgb_light(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 i fact *= facg; - col = fact*texcol.r; + col = fact * texcol.r; if (col > outcol.r) incol.r = col; else incol.r = outcol.r; - col = fact*texcol.g; + col = fact * texcol.g; if (col > outcol.g) incol.g = col; else incol.g = outcol.g; - col = fact*texcol.b; + col = fact * texcol.b; if (col > outcol.b) incol.b = col; else incol.b = outcol.b; } @@ -1076,7 +1112,7 @@ void mtex_rgb_hue(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc { vec4 col; - mix_hue(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_hue(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1084,7 +1120,7 @@ void mtex_rgb_sat(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc { vec4 col; - mix_sat(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_sat(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1092,7 +1128,7 @@ void mtex_rgb_val(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc { vec4 col; - mix_val(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_val(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1100,7 +1136,7 @@ void mtex_rgb_color(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 i { vec4 col; - mix_color(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_color(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1109,11 +1145,11 @@ void mtex_rgb_soft(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 in float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; vec3 one = vec3(1.0); - vec3 scr = one - (one - texcol)*(one - outcol); - incol = facm*outcol + fact*((one - texcol)*outcol*texcol + outcol*scr); + vec3 scr = one - (one - texcol) * (one - outcol); + incol = facm * outcol + fact * ((one - texcol) * outcol * texcol + outcol * scr); } void mtex_rgb_linear(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -1121,25 +1157,25 @@ void mtex_rgb_linear(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 fact *= facg; if (texcol.r > 0.5) - incol.r = outcol.r + fact*(2.0*(texcol.r - 0.5)); + incol.r = outcol.r + fact * (2.0 * (texcol.r - 0.5)); else - incol.r = outcol.r + fact*(2.0*(texcol.r) - 1.0); + incol.r = outcol.r + fact * (2.0 * (texcol.r) - 1.0); if (texcol.g > 0.5) - incol.g = outcol.g + fact*(2.0*(texcol.g - 0.5)); + incol.g = outcol.g + fact * (2.0 * (texcol.g - 0.5)); else - incol.g = outcol.g + fact*(2.0*(texcol.g) - 1.0); + incol.g = outcol.g + fact * (2.0 * (texcol.g) - 1.0); if (texcol.b > 0.5) - incol.b = outcol.b + fact*(2.0*(texcol.b - 0.5)); + incol.b = outcol.b + fact * (2.0 * (texcol.b - 0.5)); else - incol.b = outcol.b + fact*(2.0*(texcol.b) - 1.0); + incol.b = outcol.b + fact * (2.0 * (texcol.b) - 1.0); } void mtex_value_vars(inout float fact, float facg, out float facm) { fact *= abs(facg); - facm = 1.0-fact; + facm = 1.0 - fact; if (facg < 0.0) { float tmp = fact; @@ -1153,7 +1189,7 @@ void mtex_value_blend(float outcol, float texcol, float fact, float facg, out fl float facm; mtex_value_vars(fact, facg, facm); - incol = fact*texcol + facm*outcol; + incol = fact * texcol + facm * outcol; } void mtex_value_mul(float outcol, float texcol, float fact, float facg, out float incol) @@ -1162,7 +1198,7 @@ void mtex_value_mul(float outcol, float texcol, float fact, float facg, out floa mtex_value_vars(fact, facg, facm); facm = 1.0 - facg; - incol = (facm + fact*texcol)*outcol; + incol = (facm + fact * texcol) * outcol; } void mtex_value_screen(float outcol, float texcol, float fact, float facg, out float incol) @@ -1171,7 +1207,7 @@ void mtex_value_screen(float outcol, float texcol, float fact, float facg, out f mtex_value_vars(fact, facg, facm); facm = 1.0 - facg; - incol = 1.0 - (facm + fact*(1.0 - texcol))*(1.0 - outcol); + incol = 1.0 - (facm + fact * (1.0 - texcol)) * (1.0 - outcol); } void mtex_value_sub(float outcol, float texcol, float fact, float facg, out float incol) @@ -1180,7 +1216,7 @@ void mtex_value_sub(float outcol, float texcol, float fact, float facg, out floa mtex_value_vars(fact, facg, facm); fact = -fact; - incol = fact*texcol + outcol; + incol = fact * texcol + outcol; } void mtex_value_add(float outcol, float texcol, float fact, float facg, out float incol) @@ -1189,7 +1225,7 @@ void mtex_value_add(float outcol, float texcol, float fact, float facg, out floa mtex_value_vars(fact, facg, facm); fact = fact; - incol = fact*texcol + outcol; + incol = fact * texcol + outcol; } void mtex_value_div(float outcol, float texcol, float fact, float facg, out float incol) @@ -1198,7 +1234,7 @@ void mtex_value_div(float outcol, float texcol, float fact, float facg, out floa mtex_value_vars(fact, facg, facm); if (texcol != 0.0) - incol = facm*outcol + fact*outcol/texcol; + incol = facm * outcol + fact * outcol / texcol; else incol = 0.0; } @@ -1208,7 +1244,7 @@ void mtex_value_diff(float outcol, float texcol, float fact, float facg, out flo float facm; mtex_value_vars(fact, facg, facm); - incol = facm*outcol + fact*abs(texcol - outcol); + incol = facm * outcol + fact * abs(texcol - outcol); } void mtex_value_dark(float outcol, float texcol, float fact, float facg, out float incol) @@ -1216,7 +1252,7 @@ void mtex_value_dark(float outcol, float texcol, float fact, float facg, out flo float facm; mtex_value_vars(fact, facg, facm); - incol = facm*outcol + fact*min(outcol, texcol); + incol = facm * outcol + fact * min(outcol, texcol); } void mtex_value_light(float outcol, float texcol, float fact, float facg, out float incol) @@ -1224,7 +1260,7 @@ void mtex_value_light(float outcol, float texcol, float fact, float facg, out fl float facm; mtex_value_vars(fact, facg, facm); - float col = fact*texcol; + float col = fact * texcol; if (col > outcol) incol = col; else incol = outcol; } @@ -1240,7 +1276,7 @@ void mtex_value_clamp(float fac, out float outfac) void mtex_har_divide(float har, out float outhar) { - outhar = har/128.0; + outhar = har / 128.0; } void mtex_har_multiply_clamp(float har, out float outhar) @@ -1285,15 +1321,15 @@ void mtex_rgb_invert(vec4 inrgb, out vec4 outrgb) void mtex_value_stencil(float stencil, float intensity, out float outstencil, out float outintensity) { float fact = intensity; - outintensity = intensity*stencil; - outstencil = stencil*fact; + outintensity = intensity * stencil; + outstencil = stencil * fact; } void mtex_rgb_stencil(float stencil, vec4 rgb, out float outstencil, out vec4 outrgb) { float fact = rgb.a; - outrgb = vec4(rgb.rgb, rgb.a*stencil); - outstencil = stencil*fact; + outrgb = vec4(rgb.rgb, rgb.a * stencil); + outstencil = stencil * fact; } void mtex_mapping_ofs(vec3 texco, vec3 ofs, out vec3 outtexco) @@ -1303,17 +1339,17 @@ void mtex_mapping_ofs(vec3 texco, vec3 ofs, out vec3 outtexco) void mtex_mapping_size(vec3 texco, vec3 size, out vec3 outtexco) { - outtexco = size*texco; + outtexco = size * texco; } void mtex_2d_mapping(vec3 vec, out vec3 outvec) { - outvec = vec3(vec.xy*0.5 + vec2(0.5), vec.z); + outvec = vec3(vec.xy * 0.5 + vec2(0.5), vec.z); } vec3 mtex_2d_mapping(vec3 vec) { - return vec3(vec.xy*0.5 + vec2(0.5), vec.z); + return vec3(vec.xy * 0.5 + vec2(0.5), vec.z); } void mtex_cube_map(vec3 co, samplerCube ima, out float value, out vec4 color) @@ -1347,10 +1383,10 @@ void mtex_normal(vec3 texco, sampler2D ima, out vec3 normal) // the normal used points inward. // Should this ever change this negate must be removed. vec4 color = texture2D(ima, texco.xy); - normal = 2.0*(vec3(-color.r, color.g, color.b) - vec3(-0.5, 0.5, 0.5)); + normal = 2.0 * (vec3(-color.r, color.g, color.b) - vec3(-0.5, 0.5, 0.5)); } -void mtex_bump_normals_init( vec3 vN, out vec3 vNorg, out vec3 vNacc, out float fPrevMagnitude ) +void mtex_bump_normals_init(vec3 vN, out vec3 vNorg, out vec3 vNacc, out float fPrevMagnitude) { vNorg = vN; vNacc = vN; @@ -1376,20 +1412,20 @@ void mtex_bump_init_objspace( { mat3 obj2view = to_mat3(gl_ModelViewMatrix); mat3 view2obj = to_mat3(gl_ModelViewMatrixInverse); - - vec3 vSigmaS = view2obj * dFdx( surf_pos ); - vec3 vSigmaT = view2obj * dFdy( surf_pos ); - vec3 vN = normalize( surf_norm * obj2view ); - vR1 = cross( vSigmaT, vN ); - vR2 = cross( vN, vSigmaS ) ; - fDet = dot ( vSigmaS, vR1 ); - + vec3 vSigmaS = view2obj * dFdx(surf_pos); + vec3 vSigmaT = view2obj * dFdy(surf_pos); + vec3 vN = normalize(surf_norm * obj2view); + + vR1 = cross(vSigmaT, vN); + vR2 = cross(vN, vSigmaS); + fDet = dot(vSigmaS, vR1); + /* pretransform vNacc (in mtex_bump_apply) using the inverse transposed */ vR1 = vR1 * view2obj; vR2 = vR2 * view2obj; vN = vN * view2obj; - + float fMagnitude = abs(fDet) * length(vN); vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in); fPrevMagnitude_out = fMagnitude; @@ -1401,14 +1437,14 @@ void mtex_bump_init_texturespace( out float fPrevMagnitude_out, out vec3 vNacc_out, out vec3 vR1, out vec3 vR2, out float fDet) { - vec3 vSigmaS = dFdx( surf_pos ); - vec3 vSigmaT = dFdy( surf_pos ); + vec3 vSigmaS = dFdx(surf_pos); + vec3 vSigmaT = dFdy(surf_pos); vec3 vN = surf_norm; /* normalized interpolated vertex normal */ - - vR1 = normalize( cross( vSigmaT, vN ) ); - vR2 = normalize( cross( vN, vSigmaS ) ); - fDet = sign( dot(vSigmaS, vR1) ); - + + vR1 = normalize(cross(vSigmaT, vN)); + vR2 = normalize(cross(vN, vSigmaS)); + fDet = sign(dot(vSigmaS, vR1)); + float fMagnitude = abs(fDet); vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in); fPrevMagnitude_out = fMagnitude; @@ -1420,14 +1456,14 @@ void mtex_bump_init_viewspace( out float fPrevMagnitude_out, out vec3 vNacc_out, out vec3 vR1, out vec3 vR2, out float fDet) { - vec3 vSigmaS = dFdx( surf_pos ); - vec3 vSigmaT = dFdy( surf_pos ); + vec3 vSigmaS = dFdx(surf_pos); + vec3 vSigmaT = dFdy(surf_pos); vec3 vN = surf_norm; /* normalized interpolated vertex normal */ - - vR1 = cross( vSigmaT, vN ); - vR2 = cross( vN, vSigmaS ) ; - fDet = dot ( vSigmaS, vR1 ); - + + vR1 = cross(vSigmaT, vN); + vR2 = cross(vN, vSigmaS); + fDet = dot(vSigmaS, vR1); + float fMagnitude = abs(fDet); vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in); fPrevMagnitude_out = fMagnitude; @@ -1438,14 +1474,14 @@ void mtex_bump_tap3( out float dBs, out float dBt) { vec2 STll = texco.xy; - vec2 STlr = texco.xy + dFdx(texco.xy) ; - vec2 STul = texco.xy + dFdy(texco.xy) ; - - float Hll,Hlr,Hul; - rgbtobw( texture2D(ima, STll), Hll ); - rgbtobw( texture2D(ima, STlr), Hlr ); - rgbtobw( texture2D(ima, STul), Hul ); - + vec2 STlr = texco.xy + dFdx(texco.xy); + vec2 STul = texco.xy + dFdy(texco.xy); + + float Hll, Hlr, Hul; + rgbtobw(texture2D(ima, STll), Hll); + rgbtobw(texture2D(ima, STlr), Hlr); + rgbtobw(texture2D(ima, STul), Hul); + dBs = hScale * (Hlr - Hll); dBt = hScale * (Hul - Hll); } @@ -1460,83 +1496,82 @@ void mtex_bump_bicubic( float Hr; float Hd; float Hu; - + vec2 TexDx = dFdx(texco.xy); vec2 TexDy = dFdy(texco.xy); - - vec2 STl = texco.xy - 0.5 * TexDx ; - vec2 STr = texco.xy + 0.5 * TexDx ; - vec2 STd = texco.xy - 0.5 * TexDy ; - vec2 STu = texco.xy + 0.5 * TexDy ; - + + vec2 STl = texco.xy - 0.5 * TexDx; + vec2 STr = texco.xy + 0.5 * TexDx; + vec2 STd = texco.xy - 0.5 * TexDy; + vec2 STu = texco.xy + 0.5 * TexDy; + rgbtobw(texture2D(ima, STl), Hl); rgbtobw(texture2D(ima, STr), Hr); rgbtobw(texture2D(ima, STd), Hd); rgbtobw(texture2D(ima, STu), Hu); - + vec2 dHdxy = vec2(Hr - Hl, Hu - Hd); - float fBlend = clamp(1.0-textureQueryLOD(ima, texco.xy).x, 0.0, 1.0); - if (fBlend!=0.0) - { + float fBlend = clamp(1.0 - textureQueryLOD(ima, texco.xy).x, 0.0, 1.0); + if (fBlend != 0.0) { // the derivative of the bicubic sampling of level 0 ivec2 vDim; vDim = textureSize(ima, 0); // taking the fract part of the texture coordinate is a hardcoded wrap mode. - // this is acceptable as textures use wrap mode exclusively in 3D view elsewhere in blender. + // this is acceptable as textures use wrap mode exclusively in 3D view elsewhere in blender. // this is done so that we can still get a valid texel with uvs outside the 0,1 range // by texelFetch below, as coordinates are clamped when using this function. - vec2 fTexLoc = vDim*fract(texco.xy) - vec2(0.5, 0.5); + vec2 fTexLoc = vDim * fract(texco.xy) - vec2(0.5, 0.5); ivec2 iTexLoc = ivec2(floor(fTexLoc)); - vec2 t = clamp(fTexLoc - iTexLoc, 0.0, 1.0); // sat just to be pedantic + vec2 t = clamp(fTexLoc - iTexLoc, 0.0, 1.0); // sat just to be pedantic /******************************************************************************************* * This block will replace the one below when one channel textures are properly supported. * ******************************************************************************************* - vec4 vSamplesUL = textureGather(ima, (iTexLoc+ivec2(-1,-1) + vec2(0.5,0.5))/vDim ); - vec4 vSamplesUR = textureGather(ima, (iTexLoc+ivec2(1,-1) + vec2(0.5,0.5))/vDim ); - vec4 vSamplesLL = textureGather(ima, (iTexLoc+ivec2(-1,1) + vec2(0.5,0.5))/vDim ); - vec4 vSamplesLR = textureGather(ima, (iTexLoc+ivec2(1,1) + vec2(0.5,0.5))/vDim ); + vec4 vSamplesUL = textureGather(ima, (iTexLoc+ivec2(-1,-1) + vec2(0.5,0.5))/vDim); + vec4 vSamplesUR = textureGather(ima, (iTexLoc+ivec2(1,-1) + vec2(0.5,0.5))/vDim); + vec4 vSamplesLL = textureGather(ima, (iTexLoc+ivec2(-1,1) + vec2(0.5,0.5))/vDim); + vec4 vSamplesLR = textureGather(ima, (iTexLoc+ivec2(1,1) + vec2(0.5,0.5))/vDim); mat4 H = mat4(vSamplesUL.w, vSamplesUL.x, vSamplesLL.w, vSamplesLL.x, - vSamplesUL.z, vSamplesUL.y, vSamplesLL.z, vSamplesLL.y, - vSamplesUR.w, vSamplesUR.x, vSamplesLR.w, vSamplesLR.x, - vSamplesUR.z, vSamplesUR.y, vSamplesLR.z, vSamplesLR.y); -*/ + vSamplesUL.z, vSamplesUL.y, vSamplesLL.z, vSamplesLL.y, + vSamplesUR.w, vSamplesUR.x, vSamplesLR.w, vSamplesLR.x, + vSamplesUR.z, vSamplesUR.y, vSamplesLR.z, vSamplesLR.y); + */ ivec2 iTexLocMod = iTexLoc + ivec2(-1, -1); mat4 H; - + for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { - ivec2 iTexTmp = iTexLocMod + ivec2(i,j); - + ivec2 iTexTmp = iTexLocMod + ivec2(i, j); + // wrap texture coordinates manually for texelFetch to work on uvs oitside the 0,1 range. // this is guaranteed to work since we take the fractional part of the uv above. - iTexTmp.x = (iTexTmp.x < 0)? iTexTmp.x + vDim.x : ((iTexTmp.x >= vDim.x)? iTexTmp.x - vDim.x : iTexTmp.x); - iTexTmp.y = (iTexTmp.y < 0)? iTexTmp.y + vDim.y : ((iTexTmp.y >= vDim.y)? iTexTmp.y - vDim.y : iTexTmp.y); + iTexTmp.x = (iTexTmp.x < 0) ? iTexTmp.x + vDim.x : ((iTexTmp.x >= vDim.x) ? iTexTmp.x - vDim.x : iTexTmp.x); + iTexTmp.y = (iTexTmp.y < 0) ? iTexTmp.y + vDim.y : ((iTexTmp.y >= vDim.y) ? iTexTmp.y - vDim.y : iTexTmp.y); rgbtobw(texelFetch(ima, iTexTmp, 0), H[i][j]); } } - + float x = t.x, y = t.y; float x2 = x * x, x3 = x2 * x, y2 = y * y, y3 = y2 * y; - vec4 X = vec4(-0.5*(x3+x)+x2, 1.5*x3-2.5*x2+1, -1.5*x3+2*x2+0.5*x, 0.5*(x3-x2)); - vec4 Y = vec4(-0.5*(y3+y)+y2, 1.5*y3-2.5*y2+1, -1.5*y3+2*y2+0.5*y, 0.5*(y3-y2)); - vec4 dX = vec4(-1.5*x2+2*x-0.5, 4.5*x2-5*x, -4.5*x2+4*x+0.5, 1.5*x2-x); - vec4 dY = vec4(-1.5*y2+2*y-0.5, 4.5*y2-5*y, -4.5*y2+4*y+0.5, 1.5*y2-y); - + vec4 X = vec4(-0.5 * (x3 + x) + x2, 1.5 * x3 - 2.5 * x2 + 1, -1.5 * x3 + 2 * x2 + 0.5 * x, 0.5 * (x3 - x2)); + vec4 Y = vec4(-0.5 * (y3 + y) + y2, 1.5 * y3 - 2.5 * y2 + 1, -1.5 * y3 + 2 * y2 + 0.5 * y, 0.5 * (y3 - y2)); + vec4 dX = vec4(-1.5 * x2 + 2 * x - 0.5, 4.5 * x2 - 5 * x, -4.5 * x2 + 4 * x + 0.5, 1.5 * x2 - x); + vec4 dY = vec4(-1.5 * y2 + 2 * y - 0.5, 4.5 * y2 - 5 * y, -4.5 * y2 + 4 * y + 0.5, 1.5 * y2 - y); + // complete derivative in normalized coordinates (mul by vDim) vec2 dHdST = vDim * vec2(dot(Y, H * dX), dot(dY, H * X)); // transform derivative to screen-space - vec2 dHdxy_bicubic = vec2( dHdST.x * TexDx.x + dHdST.y * TexDx.y, - dHdST.x * TexDy.x + dHdST.y * TexDy.y ); + vec2 dHdxy_bicubic = vec2(dHdST.x * TexDx.x + dHdST.y * TexDx.y, + dHdST.x * TexDy.x + dHdST.y * TexDy.y); // blend between the two - dHdxy = dHdxy*(1-fBlend) + dHdxy_bicubic*fBlend; + dHdxy = dHdxy * (1 - fBlend) + dHdxy_bicubic * fBlend; } dBs = hScale * dHdxy.x; @@ -1553,18 +1588,18 @@ void mtex_bump_tap5( vec2 TexDy = dFdy(texco.xy); vec2 STc = texco.xy; - vec2 STl = texco.xy - 0.5 * TexDx ; - vec2 STr = texco.xy + 0.5 * TexDx ; - vec2 STd = texco.xy - 0.5 * TexDy ; - vec2 STu = texco.xy + 0.5 * TexDy ; - - float Hc,Hl,Hr,Hd,Hu; - rgbtobw( texture2D(ima, STc), Hc ); - rgbtobw( texture2D(ima, STl), Hl ); - rgbtobw( texture2D(ima, STr), Hr ); - rgbtobw( texture2D(ima, STd), Hd ); - rgbtobw( texture2D(ima, STu), Hu ); - + vec2 STl = texco.xy - 0.5 * TexDx; + vec2 STr = texco.xy + 0.5 * TexDx; + vec2 STd = texco.xy - 0.5 * TexDy; + vec2 STu = texco.xy + 0.5 * TexDy; + + float Hc, Hl, Hr, Hd, Hu; + rgbtobw(texture2D(ima, STc), Hc); + rgbtobw(texture2D(ima, STl), Hl); + rgbtobw(texture2D(ima, STr), Hr); + rgbtobw(texture2D(ima, STd), Hd); + rgbtobw(texture2D(ima, STu), Hu); + dBs = hScale * (Hr - Hl); dBt = hScale * (Hu - Hd); } @@ -1573,27 +1608,27 @@ void mtex_bump_deriv( vec3 texco, sampler2D ima, float ima_x, float ima_y, float hScale, out float dBs, out float dBt) { - float s = 1.0; // negate this if flipped texture coordinate + float s = 1.0; // negate this if flipped texture coordinate vec2 TexDx = dFdx(texco.xy); vec2 TexDy = dFdy(texco.xy); - + // this variant using a derivative map is described here // http://mmikkelsen3d.blogspot.com/2011/07/derivative-maps.html vec2 dim = vec2(ima_x, ima_y); - vec2 dBduv = hScale*dim*(2.0*texture2D(ima, texco.xy).xy-1.0); - - dBs = dBduv.x*TexDx.x + s*dBduv.y*TexDx.y; - dBt = dBduv.x*TexDy.x + s*dBduv.y*TexDy.y; + vec2 dBduv = hScale * dim * (2.0 * texture2D(ima, texco.xy).xy - 1.0); + + dBs = dBduv.x * TexDx.x + s * dBduv.y * TexDx.y; + dBt = dBduv.x * TexDy.x + s * dBduv.y * TexDy.y; } void mtex_bump_apply( float fDet, float dBs, float dBt, vec3 vR1, vec3 vR2, vec3 vNacc_in, out vec3 vNacc_out, out vec3 perturbed_norm) { - vec3 vSurfGrad = sign(fDet) * ( dBs * vR1 + dBt * vR2 ); - + vec3 vSurfGrad = sign(fDet) * (dBs * vR1 + dBt * vR2); + vNacc_out = vNacc_in - vSurfGrad; - perturbed_norm = normalize( vNacc_out ); + perturbed_norm = normalize(vNacc_out); } void mtex_bump_apply_texspace( @@ -1604,12 +1639,12 @@ void mtex_bump_apply_texspace( vec2 TexDx = dFdx(texco.xy); vec2 TexDy = dFdy(texco.xy); - vec3 vSurfGrad = sign(fDet) * ( - dBs / length( vec2(ima_x*TexDx.x, ima_y*TexDx.y) ) * vR1 + - dBt / length( vec2(ima_x*TexDy.x, ima_y*TexDy.y) ) * vR2 ); + vec3 vSurfGrad = sign(fDet) * ( + dBs / length(vec2(ima_x * TexDx.x, ima_y * TexDx.y)) * vR1 + + dBt / length(vec2(ima_x * TexDy.x, ima_y * TexDy.y)) * vR2); vNacc_out = vNacc_in - vSurfGrad; - perturbed_norm = normalize( vNacc_out ); + perturbed_norm = normalize(vNacc_out); } void mtex_negate_texnormal(vec3 normal, out vec3 outnormal) @@ -1621,13 +1656,13 @@ void mtex_nspace_tangent(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 out { vec3 B = tangent.w * cross(normal, tangent.xyz); - outnormal = texnormal.x*tangent.xyz + texnormal.y*B + texnormal.z*normal; + outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal; outnormal = normalize(outnormal); } void mtex_nspace_world(mat4 viewmat, vec3 texnormal, out vec3 outnormal) { - outnormal = normalize((viewmat*vec4(texnormal, 0.0)).xyz); + outnormal = normalize((viewmat * vec4(texnormal, 0.0)).xyz); } void mtex_nspace_object(vec3 texnormal, out vec3 outnormal) @@ -1637,7 +1672,7 @@ void mtex_nspace_object(vec3 texnormal, out vec3 outnormal) void mtex_blend_normal(float norfac, vec3 normal, vec3 newnormal, out vec3 outnormal) { - outnormal = (1.0 - norfac)*normal + norfac*newnormal; + outnormal = (1.0 - norfac) * normal + norfac * newnormal; outnormal = normalize(outnormal); } @@ -1660,26 +1695,26 @@ void lamp_visibility_other(vec3 co, vec3 lampco, out vec3 lv, out float dist, ou void lamp_falloff_invlinear(float lampdist, float dist, out float visifac) { - visifac = lampdist/(lampdist + dist); + visifac = lampdist / (lampdist + dist); } void lamp_falloff_invsquare(float lampdist, float dist, out float visifac) { - visifac = lampdist/(lampdist + dist*dist); + visifac = lampdist / (lampdist + dist * dist); } void lamp_falloff_sliders(float lampdist, float ld1, float ld2, float dist, out float visifac) { - float lampdistkw = lampdist*lampdist; + float lampdistkw = lampdist * lampdist; - visifac = lampdist/(lampdist + ld1*dist); - visifac *= lampdistkw/(lampdistkw + ld2*dist*dist); + visifac = lampdist / (lampdist + ld1 * dist); + visifac *= lampdistkw / (lampdistkw + ld2 * dist * dist); } void lamp_falloff_invcoefficients(float coeff_const, float coeff_lin, float coeff_quad, float dist, out float visifac) { vec3 coeff = vec3(coeff_const, coeff_lin, coeff_quad); - vec3 d_coeff = vec3(1.0, dist, dist*dist); + vec3 d_coeff = vec3(1.0, dist, dist * dist); float visifac_r = dot(coeff, d_coeff); if (visifac_r > 0.0) visifac = 1.0 / visifac_r; @@ -1689,25 +1724,25 @@ void lamp_falloff_invcoefficients(float coeff_const, float coeff_lin, float coef void lamp_falloff_curve(float lampdist, sampler2D curvemap, float dist, out float visifac) { - visifac = texture2D(curvemap, vec2(dist/lampdist, 0.0)).x; + visifac = texture2D(curvemap, vec2(dist / lampdist, 0.0)).x; } void lamp_visibility_sphere(float lampdist, float dist, float visifac, out float outvisifac) { - float t= lampdist - dist; + float t = lampdist - dist; - outvisifac= visifac*max(t, 0.0)/lampdist; + outvisifac = visifac * max(t, 0.0) / lampdist; } void lamp_visibility_spot_square(vec3 lampvec, mat4 lampimat, vec2 scale, vec3 lv, out float inpr) { if (dot(lv, lampvec) > 0.0) { - vec3 lvrot = (lampimat*vec4(lv, 0.0)).xyz; + vec3 lvrot = (lampimat * vec4(lv, 0.0)).xyz; /* without clever non-uniform scale, we could do: */ // float x = max(abs(lvrot.x / lvrot.z), abs(lvrot.y / lvrot.z)); float x = max(abs((lvrot.x / scale.x) / lvrot.z), abs((lvrot.y / scale.y) / lvrot.z)); - inpr = 1.0/sqrt(1.0 + x*x); + inpr = 1.0 / sqrt(1.0 + x * x); } else inpr = 0.0; @@ -1742,15 +1777,15 @@ void lamp_visibility_spot(float spotsi, float spotbl, float inpr, float visifac, /* soft area */ if (spotbl != 0.0) - inpr *= smoothstep(0.0, 1.0, t/spotbl); + inpr *= smoothstep(0.0, 1.0, t / spotbl); - outvisifac = visifac*inpr; + outvisifac = visifac * inpr; } } void lamp_visibility_clamp(float visifac, out float outvisifac) { - outvisifac = (visifac < 0.001)? 0.0: visifac; + outvisifac = (visifac < 0.001) ? 0.0 : visifac; } void world_paper_view(vec3 vec, out vec3 outvec) @@ -1790,7 +1825,7 @@ void world_blend(vec3 vec, out float blend) void shade_view(vec3 co, out vec3 view) { /* handle perspective/orthographic */ - view = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(co): vec3(0.0, 0.0, -1.0); + view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(co) : vec3(0.0, 0.0, -1.0); } void shade_tangent_v(vec3 lv, vec3 tang, out vec3 vn) @@ -1813,14 +1848,14 @@ void shade_is_no_diffuse(out float is) void shade_is_hemi(float inp, out float is) { - is = 0.5*inp + 0.5; + is = 0.5 * inp + 0.5; } float area_lamp_energy(mat4 area, vec3 co, vec3 vn) { vec3 vec[4], c[4]; float rad[4], fac; - + vec[0] = normalize(co - area[0].xyz); vec[1] = normalize(co - area[1].xyz); vec[2] = normalize(co - area[2].xyz); @@ -1836,10 +1871,10 @@ float area_lamp_energy(mat4 area, vec3 co, vec3 vn) rad[2] = acos(dot(vec[2], vec[3])); rad[3] = acos(dot(vec[3], vec[0])); - fac= rad[0]*dot(vn, c[0]); - fac+= rad[1]*dot(vn, c[1]); - fac+= rad[2]*dot(vn, c[2]); - fac+= rad[3]*dot(vn, c[3]); + fac = rad[0] * dot(vn, c[0]); + fac += rad[1] * dot(vn, c[1]); + fac += rad[2] * dot(vn, c[2]); + fac += rad[3] * dot(vn, c[3]); return max(fac, 0.0); } @@ -1857,7 +1892,7 @@ void shade_inp_area( else { float intens = area_lamp_energy(area, co, vn); - inp = pow(intens*areasize, k); + inp = pow(intens * areasize, k); } } @@ -1879,8 +1914,8 @@ void shade_diffuse_oren_nayer(float nl, vec3 n, vec3 l, vec3 v, float rough, out float Lit_A = acos(realnl); float View_A = acos(nv); - vec3 Lit_B = normalize(l - realnl*n); - vec3 View_B = normalize(v - nv*n); + vec3 Lit_B = normalize(l - realnl * n); + vec3 View_B = normalize(v - nv * n); float t = max(dot(Lit_B, View_B), 0.0); @@ -1895,11 +1930,11 @@ void shade_diffuse_oren_nayer(float nl, vec3 n, vec3 l, vec3 v, float rough, out b = Lit_A; } - float A = 1.0 - (0.5*((rough*rough)/((rough*rough) + 0.33))); - float B = 0.45*((rough*rough)/((rough*rough) + 0.09)); + float A = 1.0 - (0.5 * ((rough * rough) / ((rough * rough) + 0.33))); + float B = 0.45 * ((rough * rough) / ((rough * rough) + 0.09)); b *= 0.95; - is = nl*(A + (B * t * sin(a) * tan(b))); + is = nl * (A + (B * t * sin(a) * tan(b))); } } @@ -1910,7 +1945,7 @@ void shade_diffuse_toon(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out f if (ang < size) is = 1.0; else if (ang > (size + tsmooth) || tsmooth == 0.0) is = 0.0; - else is = 1.0 - ((ang - size)/tsmooth); + else is = 1.0 - ((ang - size) / tsmooth); } void shade_diffuse_minnaert(float nl, vec3 n, vec3 v, float darkness, out float is) @@ -1922,9 +1957,9 @@ void shade_diffuse_minnaert(float nl, vec3 n, vec3 v, float darkness, out float float nv = max(dot(n, v), 0.0); if (darkness <= 1.0) - is = nl*pow(max(nv*nl, 0.1), darkness - 1.0); + is = nl * pow(max(nv * nl, 0.1), darkness - 1.0); else - is = nl*pow(1.0001 - nv, darkness - 1.0); + is = nl * pow(1.0001 - nv, darkness - 1.0); } } @@ -1933,18 +1968,18 @@ float fresnel_fac(vec3 view, vec3 vn, float grad, float fac) float t1, t2; float ffac; - if (fac==0.0) { + if (fac == 0.0) { ffac = 1.0; } else { - t1= dot(view, vn); - if (t1>0.0) t2= 1.0+t1; - else t2= 1.0-t1; + t1 = dot(view, vn); + if (t1 > 0.0) t2 = 1.0 + t1; + else t2 = 1.0 - t1; - t2= grad + (1.0-grad)*pow(t2, fac); + t2 = grad + (1.0 - grad) * pow(t2, fac); - if (t2<0.0) ffac = 0.0; - else if (t2>1.0) ffac = 1.0; + if (t2 < 0.0) ffac = 0.0; + else if (t2 > 1.0) ffac = 1.0; else ffac = t2; } @@ -1958,18 +1993,18 @@ void shade_diffuse_fresnel(vec3 vn, vec3 lv, vec3 view, float fac_i, float fac, void shade_cubic(float is, out float outis) { - if (is>0.0 && is<1.0) - outis= smoothstep(0.0, 1.0, is); + if (is > 0.0 && is < 1.0) + outis = smoothstep(0.0, 1.0, is); else - outis= is; + outis = is; } void shade_visifac(float i, float visifac, float refl, out float outi) { /*if (i > 0.0)*/ - outi = max(i*visifac*refl, 0.0); + outi = max(i * visifac * refl, 0.0); /*else - outi = i;*/ + outi = i;*/ } void shade_tangent_v_spec(vec3 tang, out vec3 vn) @@ -1980,7 +2015,7 @@ void shade_tangent_v_spec(vec3 tang, out vec3 vn) void shade_add_to_diffuse(float i, vec3 lampcol, vec3 col, out vec3 outcol) { if (i > 0.0) - outcol = i*lampcol*col; + outcol = i * lampcol * col; else outcol = vec3(0.0, 0.0, 0.0); } @@ -1991,9 +2026,9 @@ void shade_hemi_spec(vec3 vn, vec3 lv, vec3 view, float spec, float hard, float lv = normalize(lv); t = dot(vn, lv); - t = 0.5*t + 0.5; + t = 0.5 * t + 0.5; - t = visifac*spec*pow(t, hard); + t = visifac * spec * pow(t, hard); } void shade_phong_spec(vec3 n, vec3 l, vec3 v, float hard, out float specfac) @@ -2016,7 +2051,7 @@ void shade_cooktorr_spec(vec3 n, vec3 l, vec3 v, float hard, out float specfac) float nv = max(dot(n, v), 0.0); float i = pow(nh, hard); - i = i/(0.1+nv); + i = i / (0.1 + nv); specfac = i; } } @@ -2030,10 +2065,10 @@ void shade_blinn_spec(vec3 n, vec3 l, vec3 v, float refrac, float spec_power, ou specfac = 0.0; } else { - if (spec_power<100.0) - spec_power= sqrt(1.0/spec_power); + if (spec_power < 100.0) + spec_power = sqrt(1.0 / spec_power); else - spec_power= 10.0/spec_power; + spec_power = 10.0 / spec_power; vec3 h = normalize(v + l); float nh = dot(n, h); @@ -2050,8 +2085,8 @@ void shade_blinn_spec(vec3 n, vec3 l, vec3 v, float refrac, float spec_power, ou float vh = max(dot(v, h), 0.01); float a = 1.0; - float b = (2.0*nh*nv)/vh; - float c = (2.0*nh*nl)/vh; + float b = (2.0 * nh * nv) / vh; + float c = (2.0 * nh * nl) / vh; float g = 0.0; @@ -2065,7 +2100,7 @@ void shade_blinn_spec(vec3 n, vec3 l, vec3 v, float refrac, float spec_power, ou (((vh * (p - vh)) + 1.0) * ((vh * (p - vh)) + 1.0))))); float ang = acos(nh); - specfac = max(f*g*exp_blender((-(ang*ang)/(2.0*spec_power*spec_power))), 0.0); + specfac = max(f * g * exp_blender((-(ang * ang) / (2.0 * spec_power * spec_power))), 0.0); } } } @@ -2080,7 +2115,7 @@ void shade_wardiso_spec(vec3 n, vec3 l, vec3 v, float rms, out float specfac) float angle = tan(acos(nh)); float alpha = max(rms, 0.001); - specfac= nl * (1.0/(4.0*M_PI*alpha*alpha))*(exp_blender(-(angle*angle)/(alpha*alpha))/(sqrt(nv*nl))); + specfac = nl * (1.0 / (4.0 * M_PI * alpha * alpha)) * (exp_blender(-(angle * angle) / (alpha * alpha)) / (sqrt(nv * nl))); } void shade_toon_spec(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out float specfac) @@ -2091,24 +2126,24 @@ void shade_toon_spec(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out floa if (ang < size) rslt = 1.0; else if (ang >= (size + tsmooth) || tsmooth == 0.0) rslt = 0.0; - else rslt = 1.0 - ((ang - size)/tsmooth); + else rslt = 1.0 - ((ang - size) / tsmooth); specfac = rslt; } void shade_spec_area_inp(float specfac, float inp, out float outspecfac) { - outspecfac = specfac*inp; + outspecfac = specfac * inp; } void shade_spec_t(float shadfac, float spec, float visifac, float specfac, out float t) { - t = shadfac*spec*visifac*specfac; + t = shadfac * spec * visifac * specfac; } void shade_add_spec(float t, vec3 lampcol, vec3 speccol, out vec3 outcol) { - outcol = t*lampcol*speccol; + outcol = t * lampcol * speccol; } void shade_add_mirror(vec3 mir, vec4 refcol, vec3 combined, out vec3 result) @@ -2134,7 +2169,7 @@ void shade_add(vec4 col1, vec4 col2, out vec4 outcol) void shade_madd(vec4 col, vec4 col1, vec4 col2, out vec4 outcol) { - outcol = col + col1*col2; + outcol = col + col1 * col2; } void shade_add_clamped(vec4 col1, vec4 col2, out vec4 outcol) @@ -2144,52 +2179,52 @@ void shade_add_clamped(vec4 col1, vec4 col2, out vec4 outcol) void shade_madd_clamped(vec4 col, vec4 col1, vec4 col2, out vec4 outcol) { - outcol = col + max(col1*col2, vec4(0.0, 0.0, 0.0, 0.0)); + outcol = col + max(col1 * col2, vec4(0.0, 0.0, 0.0, 0.0)); } void shade_maddf(vec4 col, float f, vec4 col1, out vec4 outcol) { - outcol = col + f*col1; + outcol = col + f * col1; } void shade_mul(vec4 col1, vec4 col2, out vec4 outcol) { - outcol = col1*col2; + outcol = col1 * col2; } void shade_mul_value(float fac, vec4 col, out vec4 outcol) { - outcol = col*fac; + outcol = col * fac; } void shade_mul_value_v3(float fac, vec3 col, out vec3 outcol) { - outcol = col*fac; + outcol = col * fac; } void shade_obcolor(vec4 col, vec4 obcol, out vec4 outcol) { - outcol = vec4(col.rgb*obcol.rgb, col.a); + outcol = vec4(col.rgb * obcol.rgb, col.a); } void ramp_rgbtobw(vec3 color, out float outval) { - outval = color.r*0.3 + color.g*0.58 + color.b*0.12; + outval = color.r * 0.3 + color.g * 0.58 + color.b * 0.12; } void shade_only_shadow(float i, float shadfac, float energy, vec3 shadcol, out vec3 outshadrgb) { - outshadrgb = i*energy*(1.0 - shadfac)*(vec3(1.0)-shadcol); + outshadrgb = i * energy * (1.0 - shadfac) * (vec3(1.0) - shadcol); } void shade_only_shadow_diffuse(vec3 shadrgb, vec3 rgb, vec4 diff, out vec4 outdiff) { - outdiff = diff - vec4(rgb*shadrgb, 0.0); + outdiff = diff - vec4(rgb * shadrgb, 0.0); } void shade_only_shadow_specular(vec3 shadrgb, vec3 specrgb, vec4 spec, out vec4 outspec) { - outspec = spec - vec4(specrgb*shadrgb, 0.0); + outspec = spec - vec4(specrgb * shadrgb, 0.0); } void shade_clamp_positive(vec4 col, out vec4 outcol) @@ -2205,12 +2240,12 @@ void test_shadowbuf( result = 0.0; } else { - vec4 co = shadowpersmat*vec4(rco, 1.0); + vec4 co = shadowpersmat * vec4(rco, 1.0); //float bias = (1.5 - inp*inp)*shadowbias; - co.z -= shadowbias*co.w; - - if (co.w > 0.0 && co.x > 0.0 && co.x/co.w < 1.0 && co.y > 0.0 && co.y/co.w < 1.0) + co.z -= shadowbias * co.w; + + if (co.w > 0.0 && co.x > 0.0 && co.x / co.w < 1.0 && co.y > 0.0 && co.y / co.w < 1.0) result = shadow2DProj(shadowmap, co).x; else result = 1.0; @@ -2225,23 +2260,23 @@ void test_shadowbuf_vsm( result = 0.0; } else { - vec4 co = shadowpersmat*vec4(rco, 1.0); - if (co.w > 0.0 && co.x > 0.0 && co.x/co.w < 1.0 && co.y > 0.0 && co.y/co.w < 1.0) { + vec4 co = shadowpersmat * vec4(rco, 1.0); + if (co.w > 0.0 && co.x > 0.0 && co.x / co.w < 1.0 && co.y > 0.0 && co.y / co.w < 1.0) { vec2 moments = texture2DProj(shadowmap, co).rg; - float dist = co.z/co.w; + float dist = co.z / co.w; float p = 0.0; - + if (dist <= moments.x) p = 1.0; - float variance = moments.y - (moments.x*moments.x); - variance = max(variance, shadowbias/10.0); + float variance = moments.y - (moments.x * moments.x); + variance = max(variance, shadowbias / 10.0); float d = moments.x - dist; - float p_max = variance / (variance + d*d); + float p_max = variance / (variance + d * d); // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] - p_max = clamp((p_max-bleedbias)/(1.0-bleedbias), 0.0, 1.0); + p_max = clamp((p_max - bleedbias) / (1.0 - bleedbias), 0.0, 1.0); result = max(p, p_max); } @@ -2284,14 +2319,14 @@ void shadows_only_vsm( void shade_light_texture(vec3 rco, sampler2D cookie, mat4 shadowpersmat, out vec4 result) { - vec4 co = shadowpersmat*vec4(rco, 1.0); + vec4 co = shadowpersmat * vec4(rco, 1.0); result = texture2DProj(cookie, co); } void shade_exposure_correct(vec3 col, float linfac, float logfac, out vec3 outcol) { - outcol = linfac*(1.0 - exp(col*logfac)); + outcol = linfac * (1.0 - exp(col * logfac)); } void shade_mist_factor( @@ -2301,11 +2336,11 @@ void shade_mist_factor( if (enable == 1.0) { float fac, zcor; - zcor = (gl_ProjectionMatrix[3][3] == 0.0)? length(co): -co[2]; - + zcor = (gl_ProjectionMatrix[3][3] == 0.0) ? length(co) : -co[2]; + fac = clamp((zcor - miststa) / mistdist, 0.0, 1.0); if (misttype == 0.0) fac *= fac; - else if (misttype == 1.0); + else if (misttype == 1.0) ; else fac = sqrt(fac); outfac = 1.0 - (1.0 - fac) * (1.0 - misi); @@ -2328,7 +2363,7 @@ void shade_alpha_opaque(vec4 col, out vec4 outcol) void shade_alpha_obcolor(vec4 col, vec4 obcol, out vec4 outcol) { - outcol = vec4(col.rgb, col.a*obcol.a); + outcol = vec4(col.rgb, col.a * obcol.a); } /*********** NEW SHADER UTILITIES **************/ @@ -2343,9 +2378,9 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) if (g > 0.0) { g = sqrt(g); - float A =(g - c)/(g + c); - float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0); - result = 0.5 * A * A *(1.0 + B * B); + float A = (g - c) / (g + c); + float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0); + result = 0.5 * A * A * (1.0 + B * B); } else { result = 1.0; /* TIR (no refracted component) */ @@ -2356,7 +2391,7 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) float hypot(float x, float y) { - return sqrt(x*x + y*y); + return sqrt(x * x + y * y); } void generated_from_orco(vec3 orco, out vec3 generated) @@ -2364,6 +2399,17 @@ void generated_from_orco(vec3 orco, out vec3 generated) generated = orco * 0.5 + vec3(0.5); } +int floor_to_int(float x) +{ + return int(floor(x)); +} + +int quick_floor(float x) +{ + return int(x) - ((x < 0) ? 1 : 0); +} + +#ifdef BIT_OPERATIONS float integer_noise(int n) { int nn; @@ -2373,24 +2419,18 @@ float integer_noise(int n) return 0.5 * (float(nn) / 1073741824.0); } -int floor_to_int(float x) -{ - return int(floor(x)); -} - -#ifdef BIT_OPERATIONS uint hash(uint kx, uint ky, uint kz) { -#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) -#define final(a,b,c) \ +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) +#define final(a, b, c) \ { \ - c ^= b; c -= rot(b,14); \ - a ^= c; a -= rot(c,11); \ - b ^= a; b -= rot(a,25); \ - c ^= b; c -= rot(b,16); \ - a ^= c; a -= rot(c,4); \ - b ^= a; b -= rot(a,14); \ - c ^= b; c -= rot(b,24); \ + c ^= b; c -= rot(b, 14); \ + a ^= c; a -= rot(c, 11); \ + b ^= a; b -= rot(a, 25); \ + c ^= b; c -= rot(b, 16); \ + a ^= c; a -= rot(c, 4); \ + b ^= a; b -= rot(a, 14); \ + c ^= b; c -= rot(b, 24); \ } // now hash the data! uint a, b, c, len = 3u; @@ -2399,7 +2439,7 @@ uint hash(uint kx, uint ky, uint kz) c += kz; b += ky; a += kx; - final(a, b, c); + final (a, b, c); return c; #undef rot @@ -2419,9 +2459,9 @@ float bits_to_01(uint bits) float cellnoise(vec3 p) { - int ix = floor_to_int(p.x); - int iy = floor_to_int(p.y); - int iz = floor_to_int(p.z); + int ix = quick_floor(p.x); + int iy = quick_floor(p.y); + int iz = quick_floor(p.z); return bits_to_01(hash(uint(ix), uint(iy), uint(iz))); } @@ -2459,10 +2499,10 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out vec4 result) vec3 light_diffuse = gl_LightSource[i].diffuse.rgb; float bsdf = max(dot(N, light_position), 0.0); - L += light_diffuse*bsdf; + L += light_diffuse * bsdf; } - result = vec4(L*color.rgb, 1.0); + result = vec4(L * color.rgb, 1.0); } void node_bsdf_glossy(vec4 color, float roughness, vec3 N, out vec4 result) @@ -2478,12 +2518,12 @@ void node_bsdf_glossy(vec4 color, float roughness, vec3 N, out vec4 result) vec3 light_specular = gl_LightSource[i].specular.rgb; /* we mix in some diffuse so low roughness still shows up */ - float bsdf = 0.5*pow(max(dot(N, H), 0.0), 1.0/roughness); - bsdf += 0.5*max(dot(N, light_position), 0.0); - L += light_specular*bsdf; + float bsdf = 0.5 * pow(max(dot(N, H), 0.0), 1.0 / roughness); + bsdf += 0.5 * max(dot(N, light_position), 0.0); + L += light_specular * bsdf; } - result = vec4(L*color.rgb, 1.0); + result = vec4(L * color.rgb, 1.0); } void node_bsdf_anisotropic( @@ -2548,7 +2588,7 @@ void node_ambient_occlusion(vec4 color, out vec4 result) void node_emission(vec4 color, float strength, vec3 N, out vec4 result) { - result = color*strength; + result = color * strength; } /* background */ @@ -2564,7 +2604,7 @@ void background_transform_to_world(vec3 viewvec, out vec3 worldvec) void node_background(vec4 color, float strength, vec3 N, out vec4 result) { - result = color*strength; + result = color * strength; } /* closures */ @@ -2584,10 +2624,10 @@ void node_add_shader(vec4 shader1, vec4 shader2, out vec4 shader) void node_fresnel(float ior, vec3 N, vec3 I, out float result) { /* handle perspective/orthographic */ - vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(I): vec3(0.0, 0.0, -1.0); + vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); float eta = max(ior, 0.00001); - result = fresnel_dielectric(I_view, N, (gl_FrontFacing)? eta: 1.0/eta); + result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0 / eta); } /* layer_weight */ @@ -2596,15 +2636,15 @@ void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float { /* fresnel */ float eta = max(1.0 - blend, 0.00001); - vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(I): vec3(0.0, 0.0, -1.0); + vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); - fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing)? 1.0/eta : eta ); + fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? 1.0 / eta : eta); /* facing */ facing = abs(dot(I_view, N)); if (blend != 0.5) { blend = clamp(blend, 0.0, 0.99999); - blend = (blend < 0.5)? 2.0*blend: 0.5/(1.0 - blend); + blend = (blend < 0.5) ? 2.0 * blend : 0.5 / (1.0 - blend); facing = pow(facing, blend); } facing = 1.0 - facing; @@ -2633,7 +2673,7 @@ void node_attribute(vec3 attr, out vec4 outcol, out vec3 outvec, out float outf) */ outcol = vec4(attr, 1.0); outvec = attr; - outf = (attr.x + attr.y + attr.z)/3.0; + outf = (attr.x + attr.y + attr.z) / 3.0; } void node_uvmap(vec3 attr_uv, out vec3 outvec) @@ -2641,48 +2681,51 @@ void node_uvmap(vec3 attr_uv, out vec3 outvec) outvec = attr_uv; } -void node_geometry(vec3 I, vec3 N, mat4 toworld, - out vec3 position, out vec3 normal, out vec3 tangent, - out vec3 true_normal, out vec3 incoming, out vec3 parametric, - out float backfacing, out float pointiness) +void node_geometry( + vec3 I, vec3 N, mat4 toworld, + out vec3 position, out vec3 normal, out vec3 tangent, + out vec3 true_normal, out vec3 incoming, out vec3 parametric, + out float backfacing, out float pointiness) { - position = (toworld*vec4(I, 1.0)).xyz; - normal = (toworld*vec4(N, 0.0)).xyz; + position = (toworld * vec4(I, 1.0)).xyz; + normal = (toworld * vec4(N, 0.0)).xyz; tangent = vec3(0.0); true_normal = normal; /* handle perspective/orthographic */ - vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(I): vec3(0.0, 0.0, -1.0); - incoming = -(toworld*vec4(I_view, 0.0)).xyz; + vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0); + incoming = -(toworld * vec4(I_view, 0.0)).xyz; parametric = vec3(0.0); - backfacing = (gl_FrontFacing)? 0.0: 1.0; + backfacing = (gl_FrontFacing) ? 0.0 : 1.0; pointiness = 0.0; } -void node_tex_coord(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, - vec3 attr_orco, vec3 attr_uv, - out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, - out vec3 camera, out vec3 window, out vec3 reflection) +void node_tex_coord( + vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, + vec3 attr_orco, vec3 attr_uv, + out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, + out vec3 camera, out vec3 window, out vec3 reflection) { generated = attr_orco * 0.5 + vec3(0.5); - normal = normalize((obinvmat*(viewinvmat*vec4(N, 0.0))).xyz); + normal = normalize((obinvmat * (viewinvmat * vec4(N, 0.0))).xyz); uv = attr_uv; - object = (obinvmat*(viewinvmat*vec4(I, 1.0))).xyz; + object = (obinvmat * (viewinvmat * vec4(I, 1.0))).xyz; camera = vec3(I.xy, -I.z); vec4 projvec = gl_ProjectionMatrix * vec4(I, 1.0); - window = vec3(mtex_2d_mapping(projvec.xyz/projvec.w).xy * camerafac.xy + camerafac.zw, 0.0); + window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0); vec3 shade_I; shade_view(I, shade_I); vec3 view_reflection = reflect(shade_I, normalize(N)); - reflection = (viewinvmat*vec4(view_reflection, 0.0)).xyz; + reflection = (viewinvmat * vec4(view_reflection, 0.0)).xyz; } -void node_tex_coord_background(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, - vec3 attr_orco, vec3 attr_uv, - out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, - out vec3 camera, out vec3 window, out vec3 reflection) +void node_tex_coord_background( + vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, + vec3 attr_orco, vec3 attr_uv, + out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, + out vec3 camera, out vec3 window, out vec3 reflection) { vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); vec4 co_homogenous = (gl_ProjectionMatrixInverse * v); @@ -2698,9 +2741,9 @@ void node_tex_coord_background(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, v object = coords; camera = vec3(co.xy, -co.z); - window = (gl_ProjectionMatrix[3][3] == 0.0) ? - vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0) : - vec3(vec2(0.5) * camerafac.xy + camerafac.zw, 0.0); + window = (gl_ProjectionMatrix[3][3] == 0.0) ? + vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0) : + vec3(vec2(0.5) * camerafac.xy + camerafac.zw, 0.0); reflection = -coords; } @@ -2718,12 +2761,12 @@ float calc_gradient(vec3 p, int gradient_type) } else if (gradient_type == 1) { /* quadratic */ float r = max(x, 0.0); - return r*r; + return r * r; } else if (gradient_type == 2) { /* easing */ float r = min(max(x, 0.0), 1.0); - float t = r*r; - return (3.0*t - 2.0*t*r); + float t = r * r; + return (3.0 * t - 2.0 * t * r); } else if (gradient_type == 3) { /* diagonal */ return (x + y) * 0.5; @@ -2732,9 +2775,9 @@ float calc_gradient(vec3 p, int gradient_type) return atan(y, x) / (M_PI * 2) + 0.5; } else { - float r = max(1.0 - sqrt(x*x + y*y + z*z), 0.0); + float r = max(1.0 - sqrt(x * x + y * y + z * z), 0.0); if (gradient_type == 5) { /* quadratic sphere */ - return r*r; + return r * r; } else if (gradient_type == 6) { /* sphere */ return r; @@ -2757,9 +2800,9 @@ void node_tex_checker(vec3 co, vec4 color1, vec4 color2, float scale, out vec4 c vec3 p = co * scale; /* Prevent precision issues on unit coordinates. */ - p.x = (p.x + 0.000001)*0.999999; - p.y = (p.y + 0.000001)*0.999999; - p.z = (p.z + 0.000001)*0.999999; + p.x = (p.x + 0.000001) * 0.999999; + p.y = (p.y + 0.000001) * 0.999999; + p.z = (p.z + 0.000001) * 0.999999; int xi = abs(int(floor(p.x))); int yi = abs(int(floor(p.y))); @@ -2771,6 +2814,7 @@ void node_tex_checker(vec3 co, vec4 color1, vec4 color2, float scale, out vec4 c fac = check ? 1.0 : 0.0; } +#ifdef BIT_OPERATIONS vec2 calc_brick_texture(vec3 p, float mortar_size, float bias, float brick_width, float row_height, float offset_amount, int offset_frequency, @@ -2784,19 +2828,20 @@ vec2 calc_brick_texture(vec3 p, float mortar_size, float bias, if (offset_frequency != 0 && squash_frequency != 0) { brick_width *= (rownum % squash_frequency != 0) ? 1.0 : squash_amount; /* squash */ - offset = (rownum % offset_frequency != 0) ? 0.0 : (brick_width*offset_amount); /* offset */ + offset = (rownum % offset_frequency != 0) ? 0.0 : (brick_width * offset_amount); /* offset */ } - bricknum = floor_to_int((p.x+offset) / brick_width); + bricknum = floor_to_int((p.x + offset) / brick_width); - x = (p.x+offset) - brick_width*bricknum; - y = p.y - row_height*rownum; + x = (p.x + offset) - brick_width * bricknum; + y = p.y - row_height * rownum; return vec2(clamp((integer_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias), 0.0, 1.0), (x < mortar_size || y < mortar_size || x > (brick_width - mortar_size) || y > (row_height - mortar_size)) ? 1.0 : 0.0); } +#endif void node_tex_brick(vec3 co, vec4 color1, vec4 color2, @@ -2807,7 +2852,8 @@ void node_tex_brick(vec3 co, float squash_amount, float squash_frequency, out vec4 color, out float fac) { - vec2 f2 = calc_brick_texture(co*scale, +#ifdef BIT_OPERATIONS + vec2 f2 = calc_brick_texture(co * scale, mortar_size, bias, brick_width, row_height, offset_amount, int(offset_frequency), @@ -2820,6 +2866,10 @@ void node_tex_brick(vec3 co, } color = (f == 1.0) ? mortar : color1; fac = f; +#else + color = vec4(1.0); + fac = 1.0; +#endif } void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac) @@ -2831,8 +2881,8 @@ void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac) void node_tex_environment_equirectangular(vec3 co, sampler2D ima, out vec4 color) { vec3 nco = normalize(co); - float u = -atan(nco.y, nco.x)/(2.0*M_PI) + 0.5; - float v = atan(nco.z, hypot(nco.x, nco.y))/M_PI + 0.5; + float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5; + float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5; color = texture2D(ima, vec2(u, v)); } @@ -2843,12 +2893,12 @@ void node_tex_environment_mirror_ball(vec3 co, sampler2D ima, out vec4 color) nco.y -= 1.0; - float div = 2.0*sqrt(max(-0.5*nco.y, 0.0)); + float div = 2.0 * sqrt(max(-0.5 * nco.y, 0.0)); if (div > 0.0) nco /= div; - float u = 0.5*(nco.x + 1.0); - float v = 0.5*(nco.z + 1.0); + float u = 0.5 * (nco.x + 1.0); + float v = 0.5 * (nco.z + 1.0); color = texture2D(ima, vec2(u, v)); } @@ -2864,6 +2914,82 @@ void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha) alpha = color.a; } +void node_tex_image_box(vec3 texco, + vec3 nob, + sampler2D ima, + float blend, + out vec4 color, + out float alpha) +{ + /* project from direction vector to barycentric coordinates in triangles */ + nob = vec3(abs(nob.x), abs(nob.y), abs(nob.z)); + nob /= (nob.x + nob.y + nob.z); + + /* basic idea is to think of this as a triangle, each corner representing + * one of the 3 faces of the cube. in the corners we have single textures, + * in between we blend between two textures, and in the middle we a blend + * between three textures. + * + * the Nxyz values are the barycentric coordinates in an equilateral + * triangle, which in case of blending, in the middle has a smaller + * equilateral triangle where 3 textures blend. this divides things into + * 7 zones, with an if () test for each zone */ + + vec3 weight = vec3(0.0, 0.0, 0.0); + float limit = 0.5 * (1.0 + blend); + + /* first test for corners with single texture */ + if (nob.x > limit * (nob.x + nob.y) && nob.x > limit * (nob.x + nob.z)) { + weight.x = 1.0; + } + else if (nob.y > limit * (nob.x + nob.y) && nob.y > limit * (nob.y + nob.z)) { + weight.y = 1.0; + } + else if (nob.z > limit * (nob.x + nob.z) && nob.z > limit * (nob.y + nob.z)) { + weight.z = 1.0; + } + else if (blend > 0.0) { + /* in case of blending, test for mixes between two textures */ + if (nob.z < (1.0 - limit) * (nob.y + nob.x)) { + weight.x = nob.x / (nob.x + nob.y); + weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.y = 1.0 - weight.x; + } + else if (nob.x < (1.0 - limit) * (nob.y + nob.z)) { + weight.y = nob.y / (nob.y + nob.z); + weight.y = clamp((weight.y - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.z = 1.0 - weight.y; + } + else if (nob.y < (1.0 - limit) * (nob.x + nob.z)) { + weight.x = nob.x / (nob.x + nob.z); + weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.z = 1.0 - weight.x; + } + else { + /* last case, we have a mix between three */ + weight.x = ((2.0 - limit) * nob.x + (limit - 1.0)) / (2.0 * limit - 1.0); + weight.y = ((2.0 - limit) * nob.y + (limit - 1.0)) / (2.0 * limit - 1.0); + weight.z = ((2.0 - limit) * nob.z + (limit - 1.0)) / (2.0 * limit - 1.0); + } + } + else { + /* Desperate mode, no valid choice anyway, fallback to one side.*/ + weight.x = 1.0; + } + + if (weight.x > 0.0) { + color += weight.x * texture2D(ima, texco.yz); + } + if (weight.y > 0.0) { + color += weight.y * texture2D(ima, texco.xz); + } + if (weight.z > 0.0) { + color += weight.z * texture2D(ima, texco.yx); + } + + alpha = color.a; +} + void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) { color = vec4(0.0); @@ -2873,42 +2999,42 @@ void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) void node_tex_magic(vec3 co, float scale, float distortion, float depth, out vec4 color, out float fac) { vec3 p = co * scale; - float x = sin((p.x + p.y + p.z)*5.0); - float y = cos((-p.x + p.y - p.z)*5.0); - float z = -cos((-p.x - p.y + p.z)*5.0); + float x = sin((p.x + p.y + p.z) * 5.0); + float y = cos((-p.x + p.y - p.z) * 5.0); + float z = -cos((-p.x - p.y + p.z) * 5.0); if (depth > 0) { x *= distortion; y *= distortion; z *= distortion; - y = -cos(x-y+z); + y = -cos(x - y + z); y *= distortion; if (depth > 1) { - x = cos(x-y-z); + x = cos(x - y - z); x *= distortion; if (depth > 2) { - z = sin(-x-y-z); + z = sin(-x - y - z); z *= distortion; if (depth > 3) { - x = -cos(-x+y-z); + x = -cos(-x + y - z); x *= distortion; if (depth > 4) { - y = -sin(-x+y+z); + y = -sin(-x + y + z); y *= distortion; if (depth > 5) { - y = -cos(-x+y+z); + y = -cos(-x + y + z); y *= distortion; if (depth > 6) { - x = cos(x+y+z); + x = cos(x + y + z); x *= distortion; if (depth > 7) { - z = sin(x+y-z); + z = sin(x + y - z); z *= distortion; if (depth > 8) { - x = -cos(-x-y+z); + x = -cos(-x - y + z); x *= distortion; if (depth > 9) { - y = -sin(x-y+z); + y = -sin(x - y + z); y *= distortion; } } @@ -2927,7 +3053,7 @@ void node_tex_magic(vec3 co, float scale, float distortion, float depth, out vec z /= distortion; } - color = vec4(0.5 - x, 0.5 - y, 0.f - z, 1.0); + color = vec4(0.5 - x, 0.5 - y, 0.5 - z, 1.0); fac = (color.x + color.y + color.z) / 3.0; } @@ -2968,14 +3094,14 @@ float noise_perlin(float x, float y, float z) float result; - result = noise_nerp(w, noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z), fx, fy, fz ), - noise_grad(hash(X+1, Y, Z), fx-1.0, fy, fz)), - noise_nerp(u, noise_grad(hash(X, Y+1, Z), fx, fy - 1.0, fz), - noise_grad(hash(X+1, Y+1, Z), fx-1.0, fy-1.0, fz))), - noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z+1), fx, fy, fz-1.0), - noise_grad(hash(X+1, Y, Z+1), fx-1.0, fy, fz-1.0)), - noise_nerp(u, noise_grad(hash(X, Y+1, Z+1), fx, fy-1.0, fz-1.0), - noise_grad(hash(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0)))); + result = noise_nerp(w, noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z), fx, fy, fz), + noise_grad(hash(X + 1, Y, Z), fx - 1.0, fy, fz)), + noise_nerp(u, noise_grad(hash(X, Y + 1, Z), fx, fy - 1.0, fz), + noise_grad(hash(X + 1, Y + 1, Z), fx - 1.0, fy - 1.0, fz))), + noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1.0), + noise_grad(hash(X + 1, Y, Z + 1), fx - 1.0, fy, fz - 1.0)), + noise_nerp(u, noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1.0, fz - 1.0), + noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1.0, fy - 1.0, fz - 1.0)))); return noise_scale3(result); } @@ -2998,27 +3124,27 @@ float noise_turbulence(vec3 p, float octaves, int hard) octaves = clamp(octaves, 0.0, 16.0); n = int(octaves); for (i = 0; i <= n; i++) { - float t = noise(fscale*p); + float t = noise(fscale * p); if (hard != 0) { - t = abs(2.0*t - 1.0); + t = abs(2.0 * t - 1.0); } - sum += t*amp; + sum += t * amp; amp *= 0.5; fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { - float t = noise(fscale*p); + float t = noise(fscale * p); if (hard != 0) { - t = abs(2.0*t - 1.0); + t = abs(2.0 * t - 1.0); } - float sum2 = sum + t*amp; - sum *= (float(1 << n) / float((1 << (n+1)) - 1)); - sum2 *= (float(1 << (n+1)) / float((1 << (n+2)) - 1)); - return (1.0 - rmd)*sum + rmd*sum2; + float sum2 = sum + t * amp; + sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); + sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1)); + return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= (float(1 << n) / float((1 << (n+1)) - 1)); + sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); return sum; } } @@ -3224,15 +3350,15 @@ float svm_musgrave(int type, vec3 p) { if (type == 0 /*NODE_MUSGRAVE_MULTIFRACTAL*/) - return intensity*noise_musgrave_multi_fractal(p, dimension, lacunarity, octaves); + return intensity * noise_musgrave_multi_fractal(p, dimension, lacunarity, octaves); else if (type == 1 /*NODE_MUSGRAVE_FBM*/) - return intensity*noise_musgrave_fBm(p, dimension, lacunarity, octaves); + return intensity * noise_musgrave_fBm(p, dimension, lacunarity, octaves); else if (type == 2 /*NODE_MUSGRAVE_HYBRID_MULTIFRACTAL*/) - return intensity*noise_musgrave_hybrid_multi_fractal(p, dimension, lacunarity, octaves, offset, gain); + return intensity * noise_musgrave_hybrid_multi_fractal(p, dimension, lacunarity, octaves, offset, gain); else if (type == 3 /*NODE_MUSGRAVE_RIDGED_MULTIFRACTAL*/) - return intensity*noise_musgrave_ridged_multi_fractal(p, dimension, lacunarity, octaves, offset, gain); + return intensity * noise_musgrave_ridged_multi_fractal(p, dimension, lacunarity, octaves, offset, gain); else if (type == 4 /*NODE_MUSGRAVE_HETERO_TERRAIN*/) - return intensity*noise_musgrave_hetero_terrain(p, dimension, lacunarity, octaves, offset); + return intensity * noise_musgrave_hetero_terrain(p, dimension, lacunarity, octaves, offset); return 0.0; } #endif // #ifdef BIT_OPERATIONS @@ -3256,7 +3382,7 @@ void node_tex_musgrave(vec3 co, offset, 1.0, gain, - co*scale); + co * scale); #else fac = 1.0; #endif @@ -3353,7 +3479,7 @@ float calc_wave(vec3 p, float distortion, float detail, float detail_scale, int n = length(p) * 20.0; if (distortion != 0.0) - n += distortion * noise_turbulence(p*detail_scale, detail, 0); + n += distortion * noise_turbulence(p * detail_scale, detail, 0); if (wave_profile == 0) { /* profile sin */ return 0.5 + 0.5 * sin(n); @@ -3361,7 +3487,7 @@ float calc_wave(vec3 p, float distortion, float detail, float detail_scale, int else { /* profile saw */ n /= 2.0 * M_PI; n -= int(n); - return (n < 0.0)? n + 1.0: n; + return (n < 0.0) ? n + 1.0 : n; } } #endif // BIT_OPERATIONS @@ -3372,7 +3498,7 @@ void node_tex_wave( { #ifdef BIT_OPERATIONS float f; - f = calc_wave(co*scale, distortion, detail, detail_scale, int(wave_type), int(wave_profile)); + f = calc_wave(co * scale, distortion, detail, detail_scale, int(wave_type), int(wave_profile)); color = vec4(f, f, f, 1.0); fac = f; @@ -3451,12 +3577,12 @@ void node_bump(float strength, float dist, float height, vec3 N, vec3 surf_pos, float dHdx = dFdx(height); float dHdy = dFdy(height); - vec3 surfgrad = dHdx*Rx + dHdy*Ry; + vec3 surfgrad = dHdx * Rx + dHdy * Ry; strength = max(strength, 0.0); - result = normalize(absdet*N - dist*sign(det)*surfgrad); - result = normalize(strength*result + (1.0 - strength)*N); + result = normalize(absdet * N - dist * sign(det) * surfgrad); + result = normalize(strength * result + (1.0 - strength) * N); } /* output */ @@ -3479,7 +3605,7 @@ void material_preview_matcap(vec4 color, sampler2D ima, vec4 N, vec4 mask, out v vec2 tex; #ifndef USE_OPENSUBDIV - /* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors + /* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors * between shader stages and we want the full range of the normal */ normal = vec3(2.0, 2.0, 2.0) * vec3(N.x, N.y, N.z) - vec3(1.0, 1.0, 1.0); if (normal.z < 0.0) { diff --git a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl index 5f406c959f1..b485d2cce86 100644 --- a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl @@ -4,13 +4,13 @@ uniform sampler2D textureSource; void main() { vec4 color = vec4(0.0); - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(-3.0 * ScaleU.x, -3.0 * ScaleU.y ) ) * 0.015625; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(-2.0 * ScaleU.x, -2.0 * ScaleU.y ) ) * 0.09375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(-1.0 * ScaleU.x, -1.0 * ScaleU.y ) ) * 0.234375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(0.0, 0.0)) * 0.3125; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(1.0 * ScaleU.x, 1.0 * ScaleU.y ) ) * 0.234375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(2.0 * ScaleU.x, 2.0 * ScaleU.y ) ) * 0.09375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(3.0 * ScaleU.x, 3.0 * ScaleU.y ) ) * 0.015625; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(-3.0 * ScaleU.x, -3.0 * ScaleU.y)) * 0.015625; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(-2.0 * ScaleU.x, -2.0 * ScaleU.y)) * 0.09375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(-1.0 * ScaleU.x, -1.0 * ScaleU.y)) * 0.234375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(0.0, 0.0)) * 0.3125; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(1.0 * ScaleU.x, 1.0 * ScaleU.y)) * 0.234375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(2.0 * ScaleU.x, 2.0 * ScaleU.y)) * 0.09375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(3.0 * ScaleU.x, 3.0 * ScaleU.y)) * 0.015625; gl_FragColor = color; } diff --git a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl index 9bb2e7ad469..5d00108b052 100644 --- a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl @@ -1,6 +1,6 @@ void main() { - gl_Position = ftransform(); - gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = ftransform(); + gl_TexCoord[0] = gl_MultiTexCoord0; } diff --git a/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl b/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl index 4838289ff9e..3761bf350eb 100644 --- a/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl @@ -15,7 +15,7 @@ void main() // Adjusting moments using partial derivative float dx = dFdx(depth); float dy = dFdy(depth); - moment2 += 0.25*(dx*dx+dy*dy); + moment2 += 0.25 * (dx * dx + dy * dy); gl_FragColor = vec4(moment1, moment2, 0.0, 0.0); } diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index a4f7804cce9..6caea903bce 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -985,6 +985,7 @@ bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBas char *RNA_path_from_ID_to_struct(PointerRNA *ptr); char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop); +char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, PropertyRNA *prop, int array_dim, int index); char *RNA_path_resolve_from_type_to_property( struct PointerRNA *ptr, struct PropertyRNA *prop, diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 97c71715349..c49a6197a4e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -4642,7 +4642,47 @@ char *RNA_path_from_ID_to_struct(PointerRNA *ptr) return ptrpath; } -char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) +static void rna_path_array_multi_from_flat_index( + const int dimsize[RNA_MAX_ARRAY_LENGTH], const int totdims, + const int index_dim, int index, + int r_index_multi[RNA_MAX_ARRAY_LENGTH]) +{ + int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1]; + int i = totdims - 1; + dimsize_step[i + 1] = 1; + dimsize_step[i] = dimsize[i]; + while (--i != -1) { + dimsize_step[i] = dimsize[i] * dimsize_step[i + 1]; + } + while (++i != index_dim) { + int index_round = index / dimsize_step[i + 1]; + r_index_multi[i] = index_round; + index -= (index_round * dimsize_step[i + 1]); + } + BLI_assert(index == 0); +} + +static void rna_path_array_multi_string_from_flat_index( + PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, + char *index_str, int index_str_len) +{ + int dimsize[RNA_MAX_ARRAY_LENGTH]; + int totdims = RNA_property_array_dimension(ptr, prop, dimsize); + int index_multi[RNA_MAX_ARRAY_LENGTH]; + + rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi); + + for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) { + offset += BLI_snprintf_rlen(&index_str[offset], index_str_len - offset, "[%d]", index_multi[i]); + } +} + +/** + * \param index_dim: The dimensiuon to show, 0 disables. 1 for 1d array, 2 for 2d. etc. + * \param index: The *flattened* index to use when \a ``index_dim > 0``, + * this is expanded when used with multi-dimensional arrays. + */ +char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index) { const bool is_rna = (prop->magic == RNA_MAGIC); const char *propname; @@ -4656,25 +4696,36 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) propname = RNA_property_identifier(prop); + /* support indexing w/ multi-dimensional arrays */ + char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1]; + if (index_dim == 0) { + index_str[0] = '\0'; + } + else { + rna_path_array_multi_string_from_flat_index( + ptr, prop, index_dim, index, + index_str, sizeof(index_str)); + } + if (ptrpath) { if (is_rna) { - path = BLI_sprintfN("%s.%s", ptrpath, propname); + path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str); } else { char propname_esc[MAX_IDPROP_NAME * 2]; BLI_strescape(propname_esc, propname, sizeof(propname_esc)); - path = BLI_sprintfN("%s[\"%s\"]", ptrpath, propname_esc); + path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str); } MEM_freeN(ptrpath); } else if (RNA_struct_is_ID(ptr->type)) { if (is_rna) { - path = BLI_strdup(propname); + path = BLI_sprintfN("%s%s", propname, index_str); } else { char propname_esc[MAX_IDPROP_NAME * 2]; BLI_strescape(propname_esc, propname, sizeof(propname_esc)); - path = BLI_sprintfN("[\"%s\"]", propname_esc); + path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str); } } else { @@ -4684,6 +4735,11 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) return path; } +char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) +{ + return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1); +} + /** * \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL). */ diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index dcb4d65f3f9..678b0ac8f1f 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -131,8 +131,8 @@ static void rna_def_camera_stereo_data(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); prop = RNA_def_property(srna, "interocular_distance", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_range(prop, 0.0f, 100.0f); - RNA_def_property_ui_range(prop, 0.0f, 1.f, 1, 3); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1e4f, 1, 3); RNA_def_property_ui_text(prop, "Interocular Distance", "Set the distance between the eyes - the stereo plane distance / 30 should be fine"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3221e93fe84..bf7e4634c2f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -803,6 +803,7 @@ static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree, ReportList *reports, ntreeUpdateTree(G.main, ntree); + ED_node_tag_update_nodetree(G.main, ntree, ret->tonode); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } return ret; diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 74fb4a08eda..e1216e3c85f 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -196,6 +196,7 @@ static void rna_Scene_collada_export( int use_ngons, int use_object_instantiation, + int use_blender_profile, int sort_by_name, int export_transformation_type, int open_sim) @@ -203,7 +204,7 @@ static void rna_Scene_collada_export( collada_export(scene, filepath, apply_modifiers, export_mesh_type, selected, include_children, include_armatures, include_shapekeys, deform_bones_only, active_uv_only, include_uv_textures, include_material_textures, - use_texture_copies, use_ngons, use_object_instantiation, sort_by_name, export_transformation_type, open_sim); + use_texture_copies, use_ngons, use_object_instantiation, use_blender_profile, sort_by_name, export_transformation_type, open_sim); } #endif @@ -286,6 +287,7 @@ void RNA_api_scene(StructRNA *srna) parm = RNA_def_boolean(func, "use_ngons", 1, "Use NGons", "Keep NGons in Export"); parm = RNA_def_boolean(func, "use_object_instantiation", 1, "Use Object Instances", "Instantiate multiple Objects from same Data"); + parm = RNA_def_boolean(func, "use_blender_profile", 1, "Use Blender Profile", "Export additional Blender specific information (for material, shaders, bones, etc.)"); parm = RNA_def_boolean(func, "sort_by_name", 0, "Sort by Object name", "Sort exported data by Object name"); parm = RNA_def_boolean(func, "open_sim", 0, "Export for SL/OpenSim", "Compatibility mode for SL, OpenSim and similar online worlds"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 68d4f7f7e51..c3a66058888 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2327,6 +2327,7 @@ static void rna_def_text(StructRNA *srna) prop = RNA_def_property(srna, "font_size", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "text_size"); RNA_def_property_ui_text(prop, "Size", "Size of the text"); + RNA_def_property_range(prop, 0.0, 2000); RNA_def_property_ui_range(prop, 0.0f, 1000, 1, -1); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); diff --git a/source/blender/nodes/shader/nodes/node_shader_camera.c b/source/blender/nodes/shader/nodes/node_shader_camera.c index 49ebb15d7a4..3bdb5c36d69 100644 --- a/source/blender/nodes/shader/nodes/node_shader_camera.c +++ b/source/blender/nodes/shader/nodes/node_shader_camera.c @@ -54,7 +54,15 @@ static void node_shader_exec_camera(void *data, int UNUSED(thread), bNode *UNUSE static int gpu_shader_camera(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, "camera", in, out, GPU_builtin(GPU_VIEW_POSITION)); + GPUNodeLink *viewvec; + + viewvec = GPU_builtin(GPU_VIEW_POSITION); + + /* Blender has negative Z, Cycles positive Z convention */ + if (GPU_material_use_new_shading_nodes(mat)) + GPU_link(mat, "invert_z", viewvec, &viewvec); + + return GPU_stack_link(mat, "camera", in, out, viewvec); } void register_node_type_sh_camera(void) diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.c b/source/blender/nodes/shader/nodes/node_shader_normal_map.c index d2695609a88..85e2c77662d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.c @@ -133,29 +133,33 @@ static int gpu_shader_normal_map(GPUMaterial *mat, bNode *node, bNodeExecData *U /* **************** CYCLES ******************** */ - GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); - switch (nm->space) { case SHD_NORMAL_MAP_TANGENT: GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); GPU_link(mat, "node_normal_map", GPU_attribute(CD_TANGENT, nm->uv_map), negnorm, realnorm, &realnorm); - break; + GPU_link(mat, "vec_math_mix", strength, realnorm, GPU_builtin(GPU_VIEW_NORMAL), &out[0].link); + /* for uniform scale this is sufficient to match Cycles */ + GPU_link(mat, "direction_transform_m4v3", out[0].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[0].link); + GPU_link(mat, "vect_normalize", out[0].link, &out[0].link); + return true; case SHD_NORMAL_MAP_OBJECT: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_OBJECT_MATRIX), &realnorm); break; case SHD_NORMAL_MAP_BLENDER_OBJECT: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); GPU_link(mat, "color_to_blender_normal_new_shading", realnorm, &realnorm); GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_OBJECT_MATRIX), &realnorm); break; case SHD_NORMAL_MAP_WORLD: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); break; case SHD_NORMAL_MAP_BLENDER_WORLD: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); GPU_link(mat, "color_to_blender_normal_new_shading", realnorm, &realnorm); break; - - GPU_link(mat, "vect_normalize", realnorm, &realnorm); } } else { diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index f0a8cda045e..71200dfe9d3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -59,17 +59,49 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat Image *ima = (Image *)node->id; ImageUser *iuser = NULL; NodeTexImage *tex = node->storage; + + GPUNodeLink *norm; + int isdata = tex->color_space == SHD_COLORSPACE_NONE; + float blend = tex->projection_blend; if (!ima) return GPU_stack_link(mat, "node_tex_image_empty", in, out); - + if (!in[0].link) in[0].link = GPU_attribute(CD_MTFACE, ""); node_shader_gpu_tex_mapping(mat, node, in, out); - GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + switch (tex->projection) { + case SHD_PROJ_FLAT: + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + break; + case SHD_PROJ_BOX: + GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL), + GPU_builtin(GPU_INVERSE_VIEW_MATRIX), + &norm); + GPU_link(mat, "direction_transform_m4v3", norm, + GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), + &norm); + GPU_link(mat, "node_tex_image_box", in[0].link, + norm, + GPU_image(ima, iuser, isdata), + GPU_uniform(&blend), + &out[0].link, + &out[1].link); + break; + case SHD_PROJ_SPHERE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_sphere", in[0].link, &in[0].link); + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + break; + case SHD_PROJ_TUBE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_tube", in[0].link, &in[0].link); + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + break; + } ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if ((tex->color_space == SHD_COLORSPACE_COLOR) && diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 513c230d306..4bd30ba4dff 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -973,7 +973,9 @@ static PyObject *pyrna_prop_str(BPy_PropertyRNA *self) RNA_property_identifier(self->prop)); } -static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) +static PyObject *pyrna_prop_repr_ex( + BPy_PropertyRNA *self, + const int index_dim, const int index) { ID *id = self->ptr.id.data; PyObject *tmp_str; @@ -987,7 +989,8 @@ static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) tmp_str = PyUnicode_FromString(id->name + 2); - path = RNA_path_from_ID_to_property(&self->ptr, self->prop); + path = RNA_path_from_ID_to_property_index(&self->ptr, self->prop, index_dim, index); + if (path) { const char *data_delim = (path[0] == '[') ? "" : "."; if (GS(id->name) == ID_NT) { /* nodetree paths are not accurate */ @@ -1015,6 +1018,15 @@ static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) return ret; } +static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) +{ + return pyrna_prop_repr_ex(self, 0, -1); +} + +static PyObject *pyrna_prop_array_repr(BPy_PropertyArrayRNA *self) +{ + return pyrna_prop_repr_ex((BPy_PropertyRNA *)self, self->arraydim, self->arrayoffset); +} static PyObject *pyrna_func_repr(BPy_FunctionRNA *self) { @@ -5841,7 +5853,7 @@ PyTypeObject pyrna_prop_array_Type = { NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ - NULL, /* subclassed */ /* tp_repr */ + (reprfunc)pyrna_prop_array_repr, /* tp_repr */ /* Method suites for standard classes */ diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index a513047032a..c2b62126e5b 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -220,6 +220,7 @@ void WM_event_timer_sleep(struct wmWindowManager *wm, struct wmWindow *wi /* invoke callback, uses enum property named "type" */ void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op); int WM_operator_smooth_viewtx_get(const struct wmOperator *op); +int WM_menu_invoke_ex(struct bContext *C, struct wmOperator *op, int opcontext); int WM_menu_invoke (struct bContext *C, struct wmOperator *op, const struct wmEvent *event); int WM_enum_search_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); /* invoke callback, confirm menu + exec */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index a1233994d5c..23455f85098 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1068,7 +1068,7 @@ int WM_operator_smooth_viewtx_get(const wmOperator *op) } /* invoke callback, uses enum property named "type" */ -int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +int WM_menu_invoke_ex(bContext *C, wmOperator *op, int opcontext) { PropertyRNA *prop = op->type->prop; uiPopupMenu *pup; @@ -1090,8 +1090,8 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE); layout = UI_popup_menu_layout(pup); /* set this so the default execution context is the same as submenus */ - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN); - uiItemsFullEnumO(layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, WM_OP_EXEC_REGION_WIN, 0); + uiLayoutSetOperatorContext(layout, opcontext); + uiItemsFullEnumO(layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, opcontext, 0); UI_popup_menu_end(C, pup); return OPERATOR_INTERFACE; } @@ -1099,6 +1099,11 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) return OPERATOR_CANCELLED; } +int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN); +} + /* generic enum search invoke popup */ static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op) |