Changeset View
Standalone View
source/blender/render/intern/source/bake_api.c
| Context not available. | |||||
| bool is_smooth; | bool is_smooth; | ||||
| } TriTessFace; | } TriTessFace; | ||||
| typedef struct VertexToSort | |||||
| { | |||||
brecht: Code style convention:
```
typedef struct VertexToSort {
``` | |||||
| float pos[3]; | |||||
| short normal[3]; | |||||
| } VertexToSort; | |||||
| /** | |||||
| * Convenience structure to keep the data used for average normal projection together | |||||
| * Positions and normals are not interleaved to improve cache efficiency when binary searching | |||||
| */ | |||||
| typedef struct AverageNormalData | |||||
| { | |||||
| float *sorted_positions; | |||||
| float *sorted_normals; | |||||
| size_t count; | |||||
| } AverageNormalData; | |||||
| static int position_compare(const float p1[3], const float p2[3]) | |||||
| { | |||||
| /* Sort by X first */ | |||||
| if (p1[0] > p2[0]) { | |||||
| return 1; | |||||
| } | |||||
| else if (p1[0] < p2[0]) { | |||||
| return -1; | |||||
| } | |||||
| /* Same X, sort by Y */ | |||||
| if (p1[1] > p2[1]) { | |||||
| return 1; | |||||
| } | |||||
| else if (p1[1] < p2[1]) { | |||||
| return -1; | |||||
| } | |||||
| /* Same Y, sort by Z */ | |||||
| if (p1[2] > p2[2]) { | |||||
| return 1; | |||||
| } | |||||
| else if (p1[2] < p2[2]) { | |||||
| return -1; | |||||
| } | |||||
| /* Same X,Y,Z means it's the same position */ | |||||
| return 0; | |||||
| } | |||||
| static int vertex_to_sort_compare(const void *v1, const void *v2) | |||||
| { | |||||
| const VertexToSort x1 = *(const VertexToSort *)v1; | |||||
| const VertexToSort x2 = *(const VertexToSort *)v2; | |||||
| return position_compare(x1.pos, x2.pos); | |||||
| } | |||||
| /** | |||||
| * Find the spcified position pos into a sorted array of positions (3 components per position). Returns the index of pos or the | |||||
Done Inline Actionsspcified -> specified. brecht: `spcified` -> `specified`. | |||||
| * index before pos is crossed | |||||
| */ | |||||
| static int binary_search_position_index(const float *sorted_positions, const int sorted_positions_count, const float pos[3]) | |||||
| { | |||||
| BLI_assert(sorted_positions_count % 3 == 0); | |||||
| int mid, low = 0, high = sorted_positions_count - 1; | |||||
| if (high == low) | |||||
| return low; | |||||
| //if (sorted_positions[low * 3] >= pos) | |||||
lukasstockner97Unsubmitted Done Inline ActionsPlease use regular comments instead of pseudocode that looks like valid C to explain the algorithm. Actually in this case, a binary search should be common enough to not need any explanation... lukasstockner97: Please use regular comments instead of pseudocode that looks like valid C to explain the… | |||||
| if(vertex_to_sort_compare(&sorted_positions[low * 3], pos) != -1) | |||||
lukasstockner97Unsubmitted Done Inline ActionsWhen you're calling vertex_to_sort_compare here, the pointer to the float array sorted_positions is cast to VertexToSort* and it only works because you only access the first element which happens to be a float array as well. Just calling position_compare would be a lot cleaner. lukasstockner97: When you're calling `vertex_to_sort_compare` here, the pointer to the float array… | |||||
| return low; | |||||
| //if (sorted_positions[(high - 1) * 3] < pos) | |||||
| if(vertex_to_sort_compare(&sorted_positions[(high - 1) * 3], pos) == -1) | |||||
| return high; | |||||
| while (low < high) { | |||||
| mid = (low + high) / 2; | |||||
| //if ((sorted_positions[mid * 3] >= pos) && (sorted_positions[(mid - 1) * 3] < pos)) | |||||
| if ((vertex_to_sort_compare(&sorted_positions[mid * 3], pos) != -1) && | |||||
| (vertex_to_sort_compare(&sorted_positions[(mid - 1) * 3], pos) == -1)) | |||||
| return mid; | |||||
| //if (sorted_positions[mid * 3] > pos) { | |||||
| if(vertex_to_sort_compare(&sorted_positions[mid * 3], pos) == 1){ | |||||
| high = mid - 1; | |||||
| } | |||||
| else { | |||||
| low = mid + 1; | |||||
| } | |||||
| } | |||||
| return low; | |||||
| } | |||||
| static void find_avg_normal(const AverageNormalData *avg_normal_data, const float pos[3], float normal_out[3]) | |||||
| { | |||||
| int i = binary_search_position_index(avg_normal_data->sorted_positions, avg_normal_data->count, pos); | |||||
| copy_v3_v3(normal_out, &avg_normal_data->sorted_normals[i * 3]); | |||||
| } | |||||
| static void store_bake_pixel(void *handle, int x, int y, float u, float v) | static void store_bake_pixel(void *handle, int x, int y, float u, float v) | ||||
| { | { | ||||
| BakeDataZSpan *bd = (BakeDataZSpan *)handle; | BakeDataZSpan *bd = (BakeDataZSpan *)handle; | ||||
| Context not available. | |||||
| int primitive_id, float u, float v, | int primitive_id, float u, float v, | ||||
| float cage_extrusion, | float cage_extrusion, | ||||
| float r_co[3], float r_dir[3], | float r_co[3], float r_dir[3], | ||||
| const bool is_cage) | const bool is_cage, | ||||
| const AverageNormalData* averageNormalData) | |||||
| { | { | ||||
| float data[3][3]; | float data[3][3]; | ||||
| float coord[3]; | float coord[3]; | ||||
| float dir[3]; | float dir[3]; | ||||
| float cage[3]; | float cage[3]; | ||||
| bool is_smooth; | bool is_smooth; | ||||
| bool use_avg_normals; | |||||
| TriTessFace *triangle = &triangles[primitive_id]; | TriTessFace *triangle = &triangles[primitive_id]; | ||||
| is_smooth = triangle->is_smooth || is_cage; | is_smooth = triangle->is_smooth || is_cage; | ||||
| use_avg_normals = averageNormalData != NULL; | |||||
| copy_v3_v3(data[0], triangle->mverts[0]->co); | copy_v3_v3(data[0], triangle->mverts[0]->co); | ||||
| copy_v3_v3(data[1], triangle->mverts[1]->co); | copy_v3_v3(data[1], triangle->mverts[1]->co); | ||||
| Context not available. | |||||
| interp_barycentric_tri_v3(data, u, v, coord); | interp_barycentric_tri_v3(data, u, v, coord); | ||||
| if (is_smooth) { | if (use_avg_normals) | ||||
| { | |||||
| find_avg_normal(averageNormalData, triangle->mverts[0]->co, data[0]); | |||||
| find_avg_normal(averageNormalData, triangle->mverts[1]->co, data[1]); | |||||
| find_avg_normal(averageNormalData, triangle->mverts[2]->co, data[2]); | |||||
| interp_barycentric_tri_v3(data, u, v, dir); | |||||
| normalize_v3(dir); | |||||
| } | |||||
| else if (is_smooth) { | |||||
| normal_short_to_float_v3(data[0], triangle->mverts[0]->no); | normal_short_to_float_v3(data[0], triangle->mverts[0]->no); | ||||
| normal_short_to_float_v3(data[1], triangle->mverts[1]->no); | normal_short_to_float_v3(data[1], triangle->mverts[1]->no); | ||||
| normal_short_to_float_v3(data[2], triangle->mverts[2]->no); | normal_short_to_float_v3(data[2], triangle->mverts[2]->no); | ||||
| Context not available. | |||||
| return triangles; | return triangles; | ||||
| } | } | ||||
| /** | |||||
| * This function populates positions and normals from a mesh and sorts them | |||||
| * Once sorted it averages normals that share the same positions together and it stores all vertices with unique positions and averaged normals | |||||
| */ | |||||
| static void populate_sorted_positions_and_average_normals(struct Mesh *me_low, TriTessFace *tris_low, AverageNormalData* average_normal_data) | |||||
| { | |||||
| size_t i, j; | |||||
lukasstockner97Unsubmitted Done Inline ActionsWe're using C99 now, so you can move those declarations to where they're actually needed (e.g. normal[3] to line 645). Loop indices make sense here though. The existing code is mostly still written for C89. lukasstockner97: We're using C99 now, so you can move those declarations to where they're actually needed (e.g. | |||||
| float accumulation_normal[3]; | |||||
| float normal[3]; | |||||
| float* sorted_positions; | |||||
| float* sorted_normals; | |||||
| const int tottri = poly_to_tri_count(me_low->totpoly, me_low->totloop); | |||||
| const int totvertices = 3 * tottri; | |||||
| if (tottri == 0) | |||||
| return; | |||||
| VertexToSort* vertices_to_sort = MEM_mallocN(sizeof(VertexToSort) * totvertices, "Vertices to sort"); | |||||
| for (i = 0; i < tottri; i++) { | |||||
| vertices_to_sort[i * 3 + 0].pos[0] = tris_low[i].mverts[0]->co[0]; | |||||
lukasstockner97Unsubmitted Done Inline ActionsYou can shorten this code a lot by using copy_v3_v3 etc. and doing a loop over the three vertices per tri. lukasstockner97: You can shorten this code a lot by using `copy_v3_v3` etc. and doing a loop over the three… | |||||
| vertices_to_sort[i * 3 + 0].pos[1] = tris_low[i].mverts[0]->co[1]; | |||||
| vertices_to_sort[i * 3 + 0].pos[2] = tris_low[i].mverts[0]->co[2]; | |||||
| vertices_to_sort[i * 3 + 0].normal[0] = tris_low[i].mverts[0]->no[0]; | |||||
| vertices_to_sort[i * 3 + 0].normal[1] = tris_low[i].mverts[0]->no[1]; | |||||
| vertices_to_sort[i * 3 + 0].normal[2] = tris_low[i].mverts[0]->no[2]; | |||||
| vertices_to_sort[i * 3 + 1].pos[0] = tris_low[i].mverts[1]->co[0]; | |||||
| vertices_to_sort[i * 3 + 1].pos[1] = tris_low[i].mverts[1]->co[1]; | |||||
| vertices_to_sort[i * 3 + 1].pos[2] = tris_low[i].mverts[1]->co[2]; | |||||
| vertices_to_sort[i * 3 + 1].normal[0] = tris_low[i].mverts[1]->no[0]; | |||||
| vertices_to_sort[i * 3 + 1].normal[1] = tris_low[i].mverts[1]->no[1]; | |||||
| vertices_to_sort[i * 3 + 1].normal[2] = tris_low[i].mverts[1]->no[2]; | |||||
| vertices_to_sort[i * 3 + 2].pos[0] = tris_low[i].mverts[2]->co[0]; | |||||
| vertices_to_sort[i * 3 + 2].pos[1] = tris_low[i].mverts[2]->co[1]; | |||||
| vertices_to_sort[i * 3 + 2].pos[2] = tris_low[i].mverts[2]->co[2]; | |||||
| vertices_to_sort[i * 3 + 2].normal[0] = tris_low[i].mverts[2]->no[0]; | |||||
| vertices_to_sort[i * 3 + 2].normal[1] = tris_low[i].mverts[2]->no[1]; | |||||
| vertices_to_sort[i * 3 + 2].normal[2] = tris_low[i].mverts[2]->no[2]; | |||||
| } | |||||
| qsort(vertices_to_sort, totvertices, sizeof(VertexToSort), vertex_to_sort_compare); | |||||
| /* Allocate sorted positions for the worst case and fill the data while we count how much memory we are actually using */ | |||||
| average_normal_data->count = 0; | |||||
| average_normal_data->sorted_positions = sorted_positions = MEM_mallocN(sizeof(float) * 3 * totvertices, "Mesh vertices sorted"); | |||||
lukasstockner97Unsubmitted Done Inline ActionsThose two arrays are only references once each, I don't think the additional shortcut variable is really needed. lukasstockner97: Those two arrays are only references once each, I don't think the additional shortcut variable… | |||||
| average_normal_data->sorted_normals = sorted_normals = MEM_mallocN(sizeof(float) * 3 * totvertices, "Mesh normals sorted"); | |||||
| for (i = 0; i < totvertices; i++) { | |||||
| /* Create a new unique vertex*/ | |||||
| normal_short_to_float_v3(&accumulation_normal[0], &vertices_to_sort[i].normal[0]); | |||||
| /* Cumulate normals for all vertices ahead of us that are the same*/ | |||||
| for (j = i + 1; j < totvertices; j++) { | |||||
| if (vertex_to_sort_compare(&vertices_to_sort[i], &vertices_to_sort[j]) == 0) | |||||
| { | |||||
| normal_short_to_float_v3(&normal[0], &vertices_to_sort[j].normal[0]); | |||||
| accumulation_normal[0] += normal[0]; | |||||
| accumulation_normal[1] += normal[1]; | |||||
| accumulation_normal[2] += normal[2]; | |||||
| } | |||||
| else | |||||
| { | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* Save the vertex and move on */ | |||||
| normalize_v3(accumulation_normal); | |||||
| copy_v3_v3(&sorted_positions[(average_normal_data->count) * 3], vertices_to_sort[i].pos); | |||||
| copy_v3_v3(&sorted_normals[(average_normal_data->count) * 3], accumulation_normal); | |||||
| average_normal_data->count++; | |||||
| i = j - 1; | |||||
| } | |||||
| MEM_freeN(vertices_to_sort); | |||||
| } | |||||
| bool RE_bake_pixels_populate_from_objects( | bool RE_bake_pixels_populate_from_objects( | ||||
| struct Mesh *me_low, BakePixel pixel_array_from[], BakePixel pixel_array_to[], | struct Mesh *me_low, BakePixel pixel_array_from[], BakePixel pixel_array_to[], | ||||
| BakeHighPolyData highpoly[], const int tot_highpoly, const size_t num_pixels, const bool is_custom_cage, | BakeHighPolyData highpoly[], const int tot_highpoly, const size_t num_pixels, const bool is_custom_cage, | ||||
| const float cage_extrusion, float mat_low[4][4], float mat_cage[4][4], struct Mesh *me_cage) | const float cage_extrusion, float mat_low[4][4], float mat_cage[4][4], struct Mesh *me_cage, const bool use_average_normals) | ||||
| { | { | ||||
| size_t i; | size_t i; | ||||
| int primitive_id; | int primitive_id; | ||||
| Context not available. | |||||
| float imat_low[4][4]; | float imat_low[4][4]; | ||||
| bool is_cage = me_cage != NULL; | bool is_cage = me_cage != NULL; | ||||
| bool result = true; | bool result = true; | ||||
| AverageNormalData* average_normal_data_ptr = NULL; | |||||
lukasstockner97Unsubmitted Done Inline ActionsI think you should be able to get rid of this pointer by always passing &average_normal_data and checking average_normal_data->count > 0 in calc_point_from_barycentric_extrusion. lukasstockner97: I think you should be able to get rid of this pointer by always passing `&average_normal_data`… | |||||
| DerivedMesh *dm_low = NULL; | DerivedMesh *dm_low = NULL; | ||||
| DerivedMesh **dm_highpoly; | DerivedMesh **dm_highpoly; | ||||
| Context not available. | |||||
| TriTessFace *tris_low = NULL; | TriTessFace *tris_low = NULL; | ||||
| TriTessFace *tris_cage = NULL; | TriTessFace *tris_cage = NULL; | ||||
| TriTessFace **tris_high; | TriTessFace **tris_high; | ||||
| /* These are used if average normals are required. All vertices are collected and sorted - vertices with the same positions have the normals averaged together */ | |||||
| AverageNormalData average_normal_data; | |||||
lukasstockner97Unsubmitted Done Inline ActionsYou can shorten this initialization to just AverageNormalData average_normal_data = {0}; lukasstockner97: You can shorten this initialization to just `AverageNormalData average_normal_data = {0};` | |||||
| average_normal_data.count = 0; | |||||
| average_normal_data.sorted_normals = NULL; | |||||
| average_normal_data.sorted_positions = NULL; | |||||
| /* assume all lowpoly tessfaces can be quads */ | /* assume all lowpoly tessfaces can be quads */ | ||||
| tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array"); | tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array"); | ||||
| Context not available. | |||||
| if (!is_cage) { | if (!is_cage) { | ||||
| dm_low = CDDM_from_mesh(me_low); | dm_low = CDDM_from_mesh(me_low); | ||||
| tris_low = mesh_calc_tri_tessface(me_low, true, dm_low); | tris_low = mesh_calc_tri_tessface(me_low, true, dm_low); | ||||
| if (use_average_normals) { | |||||
| populate_sorted_positions_and_average_normals(me_low, tris_low, &average_normal_data); | |||||
| average_normal_data_ptr = &average_normal_data; | |||||
| } | |||||
| } | } | ||||
| else if (is_custom_cage) { | else if (is_custom_cage) { | ||||
| tris_low = mesh_calc_tri_tessface(me_low, false, NULL); | tris_low = mesh_calc_tri_tessface(me_low, false, NULL); | ||||
| Context not available. | |||||
| } | } | ||||
| else { | else { | ||||
| tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); | tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); | ||||
| if (use_average_normals) { | |||||
| populate_sorted_positions_and_average_normals(me_cage, tris_cage, &average_normal_data); | |||||
| average_normal_data_ptr = &average_normal_data; | |||||
| } | |||||
| } | } | ||||
| invert_m4_m4(imat_low, mat_low); | invert_m4_m4(imat_low, mat_low); | ||||
| Context not available. | |||||
| tri_low = &tris_cage[primitive_id]; | tri_low = &tris_cage[primitive_id]; | ||||
| } | } | ||||
| else if (is_cage) { | else if (is_cage) { | ||||
| calc_point_from_barycentric_extrusion(tris_cage, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, true); | calc_point_from_barycentric_extrusion(tris_cage, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, true, average_normal_data_ptr); | ||||
| tri_low = &tris_cage[primitive_id]; | tri_low = &tris_cage[primitive_id]; | ||||
| } | } | ||||
| else { | else { | ||||
| calc_point_from_barycentric_extrusion(tris_low, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, false); | calc_point_from_barycentric_extrusion(tris_low, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, false, average_normal_data_ptr); | ||||
| tri_low = &tris_low[primitive_id]; | tri_low = &tris_low[primitive_id]; | ||||
| } | } | ||||
| Context not available. | |||||
| if (tris_cage) { | if (tris_cage) { | ||||
| MEM_freeN(tris_cage); | MEM_freeN(tris_cage); | ||||
| } | } | ||||
| if (average_normal_data.sorted_positions) { | |||||
| MEM_freeN(average_normal_data.sorted_positions); | |||||
| } | |||||
| if (average_normal_data.sorted_normals) { | |||||
| MEM_freeN(average_normal_data.sorted_normals); | |||||
| } | |||||
| return result; | return result; | ||||
| } | } | ||||
| Context not available. | |||||
Code style convention:
typedef struct VertexToSort {