357 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| /** \file
 | |
|  * \ingroup edmesh
 | |
|  */
 | |
| 
 | |
| #include "MEM_guardedalloc.h"
 | |
| 
 | |
| #include "BLI_stack.h"
 | |
| #include "BLI_math.h"
 | |
| 
 | |
| #include "BKE_editmesh.h"
 | |
| 
 | |
| #include "GPU_immediate.h"
 | |
| #include "GPU_matrix.h"
 | |
| #include "GPU_state.h"
 | |
| 
 | |
| #include "ED_mesh.h"
 | |
| 
 | |
| #include "UI_resources.h"
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Mesh Edge Ring Pre-Select
 | |
|  * Public API:
 | |
|  *
 | |
|  * #EDBM_preselect_edgering_create
 | |
|  * #EDBM_preselect_edgering_destroy
 | |
|  * #EDBM_preselect_edgering_clear
 | |
|  * #EDBM_preselect_edgering_draw
 | |
|  * #EDBM_preselect_edgering_update_from_edge
 | |
|  *
 | |
|  * \{ */
 | |
| 
 | |
| static void edgering_vcos_get(BMVert *v[2][2], float r_cos[2][2][3], const float (*coords)[3])
 | |
| {
 | |
|   if (coords) {
 | |
|     int j, k;
 | |
|     for (j = 0; j < 2; j++) {
 | |
|       for (k = 0; k < 2; k++) {
 | |
|         copy_v3_v3(r_cos[j][k], coords[BM_elem_index_get(v[j][k])]);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     int j, k;
 | |
|     for (j = 0; j < 2; j++) {
 | |
|       for (k = 0; k < 2; k++) {
 | |
|         copy_v3_v3(r_cos[j][k], v[j][k]->co);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void edgering_vcos_get_pair(BMVert *v[2], float r_cos[2][3], const float (*coords)[3])
 | |
| {
 | |
|   if (coords) {
 | |
|     int j;
 | |
|     for (j = 0; j < 2; j++) {
 | |
|       copy_v3_v3(r_cos[j], coords[BM_elem_index_get(v[j])]);
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     int j;
 | |
|     for (j = 0; j < 2; j++) {
 | |
|       copy_v3_v3(r_cos[j], v[j]->co);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Given two opposite edges in a face, finds the ordering of their vertices so
 | |
|  * that cut preview lines won't cross each other.
 | |
|  */
 | |
| static void edgering_find_order(BMEdge *eed_last, BMEdge *eed, BMVert *eve_last, BMVert *v[2][2])
 | |
| {
 | |
|   BMLoop *l = eed->l;
 | |
| 
 | |
|   /* find correct order for v[1] */
 | |
|   if (!(BM_edge_in_face(eed, l->f) && BM_edge_in_face(eed_last, l->f))) {
 | |
|     BMIter liter;
 | |
|     BM_ITER_ELEM (l, &liter, l, BM_LOOPS_OF_LOOP) {
 | |
|       if (BM_edge_in_face(eed, l->f) && BM_edge_in_face(eed_last, l->f)) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* this should never happen */
 | |
|   if (!l) {
 | |
|     v[0][0] = eed->v1;
 | |
|     v[0][1] = eed->v2;
 | |
|     v[1][0] = eed_last->v1;
 | |
|     v[1][1] = eed_last->v2;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   BMLoop *l_other = BM_loop_other_edge_loop(l, eed->v1);
 | |
|   const bool rev = (l_other == l->prev);
 | |
|   while (l_other->v != eed_last->v1 && l_other->v != eed_last->v2) {
 | |
|     l_other = rev ? l_other->prev : l_other->next;
 | |
|   }
 | |
| 
 | |
|   if (l_other->v == eve_last) {
 | |
|     v[0][0] = eed->v1;
 | |
|     v[0][1] = eed->v2;
 | |
|   }
 | |
|   else {
 | |
|     v[0][0] = eed->v2;
 | |
|     v[0][1] = eed->v1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| struct EditMesh_PreSelEdgeRing {
 | |
|   float (*edges)[2][3];
 | |
|   int edges_len;
 | |
| 
 | |
|   float (*verts)[3];
 | |
|   int verts_len;
 | |
| };
 | |
| 
 | |
| struct EditMesh_PreSelEdgeRing *EDBM_preselect_edgering_create(void)
 | |
| {
 | |
|   struct EditMesh_PreSelEdgeRing *psel = MEM_callocN(sizeof(*psel), __func__);
 | |
|   return psel;
 | |
| }
 | |
| 
 | |
| void EDBM_preselect_edgering_destroy(struct EditMesh_PreSelEdgeRing *psel)
 | |
| {
 | |
|   EDBM_preselect_edgering_clear(psel);
 | |
|   MEM_freeN(psel);
 | |
| }
 | |
| 
 | |
| void EDBM_preselect_edgering_clear(struct EditMesh_PreSelEdgeRing *psel)
 | |
| {
 | |
|   MEM_SAFE_FREE(psel->edges);
 | |
|   psel->edges_len = 0;
 | |
| 
 | |
|   MEM_SAFE_FREE(psel->verts);
 | |
|   psel->verts_len = 0;
 | |
| }
 | |
| 
 | |
| void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const float matrix[4][4])
 | |
| {
 | |
|   if ((psel->edges_len == 0) && (psel->verts_len == 0)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   GPU_depth_test(false);
 | |
| 
 | |
|   GPU_matrix_push();
 | |
|   GPU_matrix_mul(matrix);
 | |
| 
 | |
|   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
 | |
| 
 | |
|   immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
 | |
|   immUniformThemeColor3(TH_GIZMO_PRIMARY);
 | |
| 
 | |
|   if (psel->edges_len > 0) {
 | |
|     immBegin(GPU_PRIM_LINES, psel->edges_len * 2);
 | |
| 
 | |
|     for (int i = 0; i < psel->edges_len; i++) {
 | |
|       immVertex3fv(pos, psel->edges[i][0]);
 | |
|       immVertex3fv(pos, psel->edges[i][1]);
 | |
|     }
 | |
| 
 | |
|     immEnd();
 | |
|   }
 | |
| 
 | |
|   if (psel->verts_len > 0) {
 | |
|     GPU_point_size(3.0f);
 | |
| 
 | |
|     immBegin(GPU_PRIM_POINTS, psel->verts_len);
 | |
| 
 | |
|     for (int i = 0; i < psel->verts_len; i++) {
 | |
|       immVertex3fv(pos, psel->verts[i]);
 | |
|     }
 | |
| 
 | |
|     immEnd();
 | |
|   }
 | |
| 
 | |
|   immUnbindProgram();
 | |
| 
 | |
|   GPU_matrix_pop();
 | |
| 
 | |
|   /* Reset default */
 | |
|   GPU_depth_test(true);
 | |
| }
 | |
| 
 | |
| static void view3d_preselect_mesh_edgering_update_verts_from_edge(
 | |
|     struct EditMesh_PreSelEdgeRing *psel,
 | |
|     BMesh *UNUSED(bm),
 | |
|     BMEdge *eed_start,
 | |
|     int previewlines,
 | |
|     const float (*coords)[3])
 | |
| {
 | |
|   float v_cos[2][3];
 | |
|   float(*verts)[3];
 | |
|   int i, tot = 0;
 | |
| 
 | |
|   verts = MEM_mallocN(sizeof(*psel->verts) * previewlines, __func__);
 | |
| 
 | |
|   edgering_vcos_get_pair(&eed_start->v1, v_cos, coords);
 | |
| 
 | |
|   for (i = 1; i <= previewlines; i++) {
 | |
|     const float fac = (i / ((float)previewlines + 1));
 | |
|     interp_v3_v3v3(verts[tot], v_cos[0], v_cos[1], fac);
 | |
|     tot++;
 | |
|   }
 | |
| 
 | |
|   psel->verts = verts;
 | |
|   psel->verts_len = previewlines;
 | |
| }
 | |
| 
 | |
| static void view3d_preselect_mesh_edgering_update_edges_from_edge(
 | |
|     struct EditMesh_PreSelEdgeRing *psel,
 | |
|     BMesh *bm,
 | |
|     BMEdge *eed_start,
 | |
|     int previewlines,
 | |
|     const float (*coords)[3])
 | |
| {
 | |
|   BMWalker walker;
 | |
|   BMEdge *eed, *eed_last;
 | |
|   BMVert *v[2][2] = {{NULL}}, *eve_last;
 | |
|   float(*edges)[2][3] = NULL;
 | |
|   BLI_Stack *edge_stack;
 | |
| 
 | |
|   int i, tot = 0;
 | |
| 
 | |
|   BMW_init(&walker,
 | |
|            bm,
 | |
|            BMW_EDGERING,
 | |
|            BMW_MASK_NOP,
 | |
|            BMW_MASK_NOP,
 | |
|            BMW_MASK_NOP,
 | |
|            BMW_FLAG_TEST_HIDDEN,
 | |
|            BMW_NIL_LAY);
 | |
| 
 | |
|   edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__);
 | |
| 
 | |
|   eed_last = NULL;
 | |
|   for (eed = eed_last = BMW_begin(&walker, eed_start); eed; eed = BMW_step(&walker)) {
 | |
|     BLI_stack_push(edge_stack, &eed);
 | |
|   }
 | |
|   BMW_end(&walker);
 | |
| 
 | |
|   eed_start = *(BMEdge **)BLI_stack_peek(edge_stack);
 | |
| 
 | |
|   edges = MEM_mallocN((sizeof(*edges) * (BLI_stack_count(edge_stack) + (eed_last != eed_start))) *
 | |
|                           previewlines,
 | |
|                       __func__);
 | |
| 
 | |
|   eve_last = NULL;
 | |
|   eed_last = NULL;
 | |
| 
 | |
|   while (!BLI_stack_is_empty(edge_stack)) {
 | |
|     BLI_stack_pop(edge_stack, &eed);
 | |
| 
 | |
|     if (eed_last) {
 | |
|       if (eve_last) {
 | |
|         v[1][0] = v[0][0];
 | |
|         v[1][1] = v[0][1];
 | |
|       }
 | |
|       else {
 | |
|         v[1][0] = eed_last->v1;
 | |
|         v[1][1] = eed_last->v2;
 | |
|         eve_last = eed_last->v1;
 | |
|       }
 | |
| 
 | |
|       edgering_find_order(eed_last, eed, eve_last, v);
 | |
|       eve_last = v[0][0];
 | |
| 
 | |
|       for (i = 1; i <= previewlines; i++) {
 | |
|         const float fac = (i / ((float)previewlines + 1));
 | |
|         float v_cos[2][2][3];
 | |
| 
 | |
|         edgering_vcos_get(v, v_cos, coords);
 | |
| 
 | |
|         interp_v3_v3v3(edges[tot][0], v_cos[0][0], v_cos[0][1], fac);
 | |
|         interp_v3_v3v3(edges[tot][1], v_cos[1][0], v_cos[1][1], fac);
 | |
|         tot++;
 | |
|       }
 | |
|     }
 | |
|     eed_last = eed;
 | |
|   }
 | |
| 
 | |
|   if ((eed_last != eed_start) &&
 | |
| #ifdef BMW_EDGERING_NGON
 | |
|       BM_edge_share_face_check(eed_last, eed_start)
 | |
| #else
 | |
|       BM_edge_share_quad_check(eed_last, eed_start)
 | |
| #endif
 | |
|   ) {
 | |
|     v[1][0] = v[0][0];
 | |
|     v[1][1] = v[0][1];
 | |
| 
 | |
|     edgering_find_order(eed_last, eed_start, eve_last, v);
 | |
| 
 | |
|     for (i = 1; i <= previewlines; i++) {
 | |
|       const float fac = (i / ((float)previewlines + 1));
 | |
|       float v_cos[2][2][3];
 | |
| 
 | |
|       if (!v[0][0] || !v[0][1] || !v[1][0] || !v[1][1]) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       edgering_vcos_get(v, v_cos, coords);
 | |
| 
 | |
|       interp_v3_v3v3(edges[tot][0], v_cos[0][0], v_cos[0][1], fac);
 | |
|       interp_v3_v3v3(edges[tot][1], v_cos[1][0], v_cos[1][1], fac);
 | |
|       tot++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   BLI_stack_free(edge_stack);
 | |
| 
 | |
|   psel->edges = edges;
 | |
|   psel->edges_len = tot;
 | |
| }
 | |
| 
 | |
| void EDBM_preselect_edgering_update_from_edge(struct EditMesh_PreSelEdgeRing *psel,
 | |
|                                               BMesh *bm,
 | |
|                                               BMEdge *eed_start,
 | |
|                                               int previewlines,
 | |
|                                               const float (*coords)[3])
 | |
| {
 | |
|   EDBM_preselect_edgering_clear(psel);
 | |
| 
 | |
|   if (coords) {
 | |
|     BM_mesh_elem_index_ensure(bm, BM_VERT);
 | |
|   }
 | |
| 
 | |
|   if (BM_edge_is_wire(eed_start)) {
 | |
|     view3d_preselect_mesh_edgering_update_verts_from_edge(
 | |
|         psel, bm, eed_start, previewlines, coords);
 | |
|   }
 | |
|   else {
 | |
|     view3d_preselect_mesh_edgering_update_edges_from_edge(
 | |
|         psel, bm, eed_start, previewlines, coords);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** \} */
 |