1 /*
2 * File: tools.c
3 *
4 * Description: performs various manipulations on verts, etc.
5 * Anything more complicated than a simple translation of a
6 * single vert should end up in here.
7 *
8 *
9 * This source code is part of kludge3d, and is released under the
10 * GNU General Public License.
11 *
12 *
13 */
14
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <math.h>
20
21
22 #include "model.h"
23 #include "tools.h"
24 #include "undo.h"
25 #include "vector.h"
26 #include "misc.h"
27 #include "selection.h"
28
29 #ifdef MEMWATCH
30 #include "memwatch.h"
31 #endif
32
33
34 /* STRUCTS **************************************************************/
35
36 typedef struct _Edge Edge;
37 struct _Edge {
38 Vertex *v1;
39 Vertex *v2;
40 int flag;
41 };
42
43
44 /* PROTOTYPES ***********************************************************/
45
46 GSList *tools_find_near_verts( GSList *verts, Vertex *v ) ;
47 void tools_find_texcoords_for_vert( Poly *p, Vertex *vert, GSList *verts,
48 float *u, float *v ) ;
49 void tools_find_texcoords_for_middlevert( Poly *p, float *u, float *v ) ;
50
51 GSList * tools_convert_edgelist_to_vertlist( GSList *edges ) ;
52 int tools_edgelist_can_be_subdivided( GSList *edges ) ;
53 GSList * tools_sort_edge_into_bins( GSList **bins, Edge *edge ) ;
54 GSList *tools_find_outside_edges( GSList *polys ) ;
55 gboolean tools_edges_isNotEqual( Edge *e1, Edge *e2 ) ;
56 gboolean tools_edges_areConnected( Edge *e1, Edge *e2 ) ;
57 gboolean tools_edge_hasVertex( Edge *e, Vertex *v ) ;
58
59
60 /* VERTEX TOOLS *********************************************************/
61
tools_vertices_move(Model * model,GSList * verts,float deltaX,float deltaY,float deltaZ)62 void tools_vertices_move( Model *model, GSList *verts, float deltaX, float deltaY, float deltaZ ) {
63
64 for ( ; verts != NULL; verts = verts->next ) {
65 // go through all the vertices and move them
66
67 vertex_move( ( Vertex * ) verts->data, deltaX, deltaY, deltaZ );
68 }
69 }
70
71
tools_vertices_snap_to_grid(Model * model,GSList * verts,float spacing)72 void tools_vertices_snap_to_grid( Model *model, GSList * verts, float spacing ) {
73
74 Vertex * v = NULL;
75 float mult;
76 int i;
77
78 while( verts != NULL ) {
79 v = (Vertex*)verts->data;
80
81 for( i = 0; i < 3; i++ ) {
82 mult = v->v[i] / spacing;
83 v->v[i] = spacing * rint( mult );
84 }
85
86 verts = verts->next;
87 }
88
89 action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
90 }
91
92
tools_vertices_snap_together(Model * model,GSList * plist)93 void tools_vertices_snap_together( Model *model, GSList * plist ) {
94
95 GSList * pts = NULL;
96 Vertex average;
97 Vertex * p = NULL;
98 int i;
99
100 vertices_find_mean( plist, &average );
101
102 pts = plist;
103 while( pts != NULL ) {
104 p = (Vertex*)pts->data;
105
106 for( i = 0; i < 3; i++ ) {
107 p->v[i] = average.v[i];
108 }
109
110 pts = pts->next;
111 }
112
113 action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
114 }
115
116
tools_vertices_snap_together_along_axis(Model * model,GSList * plist,int axis)117 void tools_vertices_snap_together_along_axis( Model *model, GSList * plist, int axis ) {
118
119 GSList * pts = NULL;
120 Vertex average;
121 Vertex * p = NULL;
122 int i;
123
124 vertices_find_mean( plist, &average );
125
126 pts = plist;
127 while( pts != NULL ) {
128 p = (Vertex*)pts->data;
129
130 // align vertices along active axis only
131 for( i = 0; i < 3; i++ ) {
132 if( i != axis )
133 p->v[i] = average.v[i];
134 }
135
136 pts = pts->next;
137 }
138
139 action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
140 }
141
142
tools_vertices_rotate_90(Model * model,GSList * pts,int axis)143 void tools_vertices_rotate_90( Model *model, GSList * pts, int axis ) {
144
145 float tempf[4];
146 int tempi[1];
147
148 tempf[0] = tempf[1] = tempf[2] = 0.0f;
149 tempf[3] = M_PI / 2.0;
150 tempi[0] = axis;
151
152 action_do( model, ACTION_VERTEX_ROT, pts, NULL, NULL, tempf, tempi, NULL );
153
154 while( pts != NULL ) {
155
156 Vertex* p = (Vertex*)(pts->data);
157 float temp;
158
159 if( axis == 0 ) {
160 temp = p->v[1];
161 p->v[1] = p->v[2];
162 p->v[2] = temp * -1.0;
163 } else if( axis == 1 ) {
164 temp = p->v[0];
165 p->v[0] = p->v[2];
166 p->v[2] = temp * -1.0;
167 } else if( axis == 2 ) {
168 temp = p->v[0];
169 p->v[0] = p->v[1];
170 p->v[1] = temp * -1.0;
171 }
172
173 pts = pts->next;
174 }
175
176 }
177
tools_vertices_rotate(Model * model,GSList * pts,int axis,float angle)178 void tools_vertices_rotate( Model *model, GSList * pts, int axis, float angle ) {
179
180 float tempf[4];
181 int tempi[1];
182 float cosine = cos(angle);
183 float sine = sin(angle);
184
185 tempf[0] = tempf[1] = tempf[2] = 0.0f;
186 tempf[3] = angle;
187 tempi[0] = axis;
188
189 action_do( model, ACTION_VERTEX_ROT, pts, NULL, NULL, tempf, tempi, NULL );
190
191 while( pts != NULL ) {
192
193 Vertex* p = (Vertex*)(pts->data);
194 float temp0, temp1, temp2;
195
196 if( axis == 0 ) {
197 temp1 = p->v[1];
198 temp2 = p->v[2];
199
200 p->v[1] = temp1*cosine - temp2*sine;
201 p->v[2] = temp1*sine + temp2*cosine;
202 } else if( axis == 1 ) {
203 temp0 = p->v[0];
204 temp2 = p->v[2];
205
206 p->v[0] = temp0*cosine + temp2*sine;
207 p->v[2] = - temp0*sine + temp2*cosine;
208 } else if( axis == 2 ) {
209 temp0 = p->v[0];
210 temp1 = p->v[1];
211
212 p->v[0] = temp0*cosine - temp1*sine;
213 p->v[1] = temp0*sine + temp1*cosine;
214 }
215
216 pts = pts->next;
217 }
218
219 }
220
tools_vertices_rotate_about_point(Model * model,GSList * pts,int axis,float pt[],float angle,int register_with_undo_sys)221 void tools_vertices_rotate_about_point( Model *model,
222 GSList * pts, int axis, float pt[], float angle,
223 int register_with_undo_sys )
224 {
225
226 float tempf[4];
227 int tempi[1];
228 float cosine = cos(angle);
229 float sine = sin(angle);
230
231 if( pt == NULL )
232 return;
233
234 if( register_with_undo_sys ) {
235 vector_copy( tempf, pt );
236 tempf[3] = angle;
237 tempi[0] = axis;
238 action_do( model, ACTION_VERTEX_ROT, pts, NULL, NULL, tempf, tempi, NULL );
239 }
240
241 while( pts != NULL ) {
242
243 Vertex* p = (Vertex*)(pts->data);
244 float temp0, temp1, temp2;
245
246 if( axis == 0 ) {
247 temp1 = p->v[1] - pt[1];
248 temp2 = p->v[2] - pt[2];
249
250 p->v[1] = temp1*cosine - temp2*sine + pt[1];
251 p->v[2] = temp1*sine + temp2*cosine + pt[2];
252 } else if( axis == 1 ) {
253 temp0 = p->v[0] - pt[0];
254 temp2 = p->v[2] - pt[2];
255
256 p->v[0] = temp0*cosine + temp2*sine + pt[0];
257 p->v[2] = - temp0*sine + temp2*cosine + pt[2];
258 } else if( axis == 2 ) {
259 temp0 = p->v[0] - pt[0];
260 temp1 = p->v[1] - pt[1];
261
262 p->v[0] = temp0*cosine - temp1*sine + pt[0];
263 p->v[1] = temp0*sine + temp1*cosine + pt[1];
264 }
265
266 pts = pts->next;
267 }
268
269 }
270
tools_vertices_flip(Model * model,GSList * pts,int axis)271 void tools_vertices_flip( Model *model, GSList * pts, int axis ) {
272
273 Vertex average;
274
275 if( axis < 0 || axis > 2 )
276 return;
277
278 vertices_find_mean( pts, &average );
279
280 while( pts != NULL ) {
281
282 Vertex* p = (Vertex*)(pts->data);
283
284 p->v[axis] -= average.v[axis];
285 p->v[axis] = p->v[axis] * -1.0;
286 p->v[axis] += average.v[axis];
287
288 pts = pts->next;
289 }
290
291 action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
292 }
293
294
tools_vertices_scale_about_point(Model * model,GSList * plist,float * factor,float * point,int register_with_undo_sys)295 void tools_vertices_scale_about_point( Model *model,
296 GSList * plist, float *factor, float *point, int register_with_undo_sys )
297 {
298
299 GSList * pts = NULL;
300 Vertex * p = NULL;
301
302 if( plist == NULL ) return;
303 if( factor == NULL || point == NULL ) return;
304
305 pts = plist;
306 while( pts != NULL ) {
307 p = (Vertex*)(pts->data);
308
309 vector_sub( p->v, p->v, point );
310 vector_mul_piecewise( p->v, p->v, factor );
311 vector_add( p->v, p->v, point );
312
313 pts = pts->next;
314 }
315
316 if( register_with_undo_sys ) {
317 float temp[6];
318 vector_copy( temp, factor );
319 vector_copy( temp + 3, point );
320 action_do( model, ACTION_VERTEX_SCALE,
321 (plist == model->selected_verts ? NULL : plist),
322 NULL, NULL, temp, NULL, NULL );
323 }
324
325 }
326
327
tools_vertices_scale_about_center(Model * model,GSList * plist,float * factor,int register_with_undo_sys)328 void tools_vertices_scale_about_center( Model *model,
329 GSList * plist, float *factor, int register_with_undo_sys )
330 {
331 Vertex average;
332 vertices_find_mean( plist, &average );
333 tools_vertices_scale_about_point( model,
334 plist, factor, average.v, register_with_undo_sys );
335 }
336
337
tools_vertices_scale_symmetrically_about_center(Model * model,GSList * plist,float factor,int register_with_undo_sys)338 void tools_vertices_scale_symmetrically_about_center( Model *model,
339 GSList * plist, float factor, int register_with_undo_sys )
340 {
341 float f_v[3];
342 f_v[0] = f_v[1] = f_v[2] = factor;
343 tools_vertices_scale_about_center( model, plist, f_v, register_with_undo_sys );
344 }
345
346
tools_vertices_scale_about_origin(Model * model,GSList * plist,float factor,int register_with_undo_sys)347 void tools_vertices_scale_about_origin( Model *model,
348 GSList * plist, float factor, int register_with_undo_sys )
349 {
350 float f_v[3], pt[3];
351 f_v[0] = f_v[1] = f_v[2] = factor;
352 pt[0] = pt[1] = pt[2] = 0.0;
353 tools_vertices_scale_about_point( model, plist, f_v, pt, register_with_undo_sys );
354 }
355
356
tools_vertices_weld_list(Model * model,GSList * verts)357 void tools_vertices_weld_list( Model *model, GSList *verts ) {
358
359 GSList *l;
360 Vertex *first;
361
362 if( verts == NULL ) return;
363 if( g_slist_length( verts ) < 2 ) return;
364
365 tools_vertices_snap_together( model, verts );
366
367 l = verts;
368 first = (Vertex*)l->data;
369 l = l->next;
370 for( ; l; l = l->next ) {
371 tools_vertices_weld( model, first, (Vertex*)l->data );
372 }
373 }
374
375
376 /* replaces all instances of v2 with v1 */
tools_vertices_weld(Model * model,Vertex * v1,Vertex * v2)377 void tools_vertices_weld( Model *model, Vertex *v1, Vertex *v2 ) {
378 int i, num_polys;
379 Poly *poly;
380 Poly **polylist;
381
382 if( v1 == NULL || v2 == NULL ) return;
383 if( v1 == v2 ) return;
384
385 /*
386 for every poly using v2
387 if poly uses both v1 & v2
388 remove v2 from poly
389 else (poly uses only v2)
390 replace v2 with v1 in poly
391 if v2 is no longer used by any polys in poly->mesh
392 remove v2 from poly->mesh
393 if poly has fewer than 3 verts
394 remove poly from model
395 remove v2 from model
396 */
397
398 num_polys = v2->num_polys;
399 polylist = (Poly**)malloc( num_polys * sizeof( Poly* ) );
400 memcpy( polylist, v2->polys, num_polys * sizeof( Poly* ) );
401
402 for( i = 0; i < num_polys; i++ ) {
403 poly = polylist[i];
404 if( poly_has_vertex( poly, v1 ) ) {
405 poly_remove_vertex( poly, v2 );
406 } else {
407 poly_replace_vertex( poly, v1,
408 array_find( (void**)poly->verts, poly->num_verts, v2 ) );
409 }
410
411 if( ! mesh_is_using_vertex( poly->mesh, v2 ) ) {
412 mesh_vertex_remove( poly->mesh, v2 );
413 }
414
415 /* yes, 3. not 2. while 2-vertex polys *are* legal, it is expected
416 behavior to remove such polys when performing a weld. */
417 if( poly->num_verts < 3 ) {
418 model_polygon_remove( model, poly );
419 }
420 }
421
422 free( polylist );
423
424 model_vertex_remove( model, v2 );
425 }
426
427
428 /* searches model for verts that are close together, and welds them */
tools_vertices_weld_modelwide(Model * model)429 void tools_vertices_weld_modelwide( Model *model ) {
430 /*
431 copy model's vert list
432 for every vert1 in copied list
433 if vert1 still exists in model vert list
434 find all verts near vert1, and weld them all together
435 */
436
437 GSList *listcopy, *l, *nearverts;
438 Vertex *v;
439
440 if( model == NULL ) return;
441 if( model->verts == NULL ) return;
442
443 listcopy = g_slist_copy( model->verts );
444 for( l = listcopy; l; l = l->next ) {
445 v = (Vertex *)l->data;
446
447 if( g_slist_find( model->verts, v ) == NULL )
448 continue;
449
450 nearverts = tools_find_near_verts( model->verts, v );
451 if( nearverts ) {
452 tools_vertices_weld_list( model, nearverts );
453 g_slist_free( nearverts );
454 }
455 }
456
457 g_slist_free( listcopy );
458 }
459
460
461 /* returns a list of all verts near v, including v */
tools_find_near_verts(GSList * verts,Vertex * v)462 GSList *tools_find_near_verts( GSList *verts, Vertex *v ) {
463 GSList *result = NULL;
464
465 if( verts == NULL || v == NULL ) return NULL;
466
467 while( verts ) {
468 verts = g_slist_find_custom( verts, v,
469 (GCompareFunc) vertices_isNotEqual );
470 if( verts ) {
471 result = g_slist_append( result, verts->data );
472 verts = verts->next;
473 }
474 }
475
476 return result;
477 }
478
479
480
481
482 /* POLYGON TOOLS ********************************************************/
483
484 /* Deprecated */
tools_polys_move(Model * model,GSList * polys,float dX,float dY,float dZ)485 void tools_polys_move( Model *model, GSList *polys, float dX, float dY, float dZ ) {
486
487 GSList *verts = NULL;
488
489 verts = polys_get_verts_unique( polys );
490
491 tools_vertices_move( model, verts, dX, dY, dZ );
492 g_slist_free( verts );
493 }
494
495
496
497 /* Deprecated */
tools_poly_move(Model * model,Poly * p,float dX,float dY,float dZ)498 void tools_poly_move( Model *model, Poly * p, float dX, float dY, float dZ ) {
499
500 int i;
501 if( p == NULL ) return;
502
503 for( i = 0; i < p->num_verts; i++ ) {
504 vertex_move( p->verts[i], dX, dY, dZ );
505 }
506 }
507
508
509 /* extrudes members of 'polys' individually. returns list of polys created,
510 * with members of 'polys' at head of list. returned list must be freed.
511 */
tools_polys_extrude(Model * model,GSList * polys)512 GSList * tools_polys_extrude( Model *model, GSList *polys ) {
513 GSList *result = NULL, *temp;
514
515 result = g_slist_copy( polys );
516
517 for( ; polys != NULL; polys = polys->next ) {
518 temp = tools_poly_extrude( model, (Poly*)polys->data );
519 /* the first item in temp is polys->data. remove it. */
520 temp = g_slist_delete_link( temp, temp );
521 result = g_slist_concat( result, temp );
522 }
523
524 return result;
525 }
526
527
528 /* extrudes poly p. returns list of polys created, with p at head of list.
529 * returned list must be freed.
530 */
tools_poly_extrude(Model * model,Poly * p)531 GSList * tools_poly_extrude( Model *model, Poly *p ) {
532 GSList *result = NULL;
533 int i;
534 Vertex *original_verts[POLY_MAX_VERTS];
535 Poly *new_poly;
536 Vertex *new_vert;
537
538 if( p == NULL )
539 return NULL;
540 if( p->mesh == NULL || p->mesh->polygons == NULL )
541 return NULL;
542
543 result = g_slist_append( result, p );
544
545 /* first, re-calc the normal, just to be safe */
546 poly_find_normal( p );
547
548 /* create n new verts, dupes of the original n */
549 for( i = 0; i < p->num_verts; i++ ) {
550 original_verts[i] = p->verts[i];
551 new_vert = vertex_dup( p->verts[i] );
552 model_vertex_add( model, new_vert );
553 mesh_vertex_add( p->mesh, new_vert );
554
555 /* replace the old with the new */
556 poly_replace_vertex( p, new_vert, i );
557 }
558
559 /* create n new polys */
560 for( i = 0; i < p->num_verts; i++ ) {
561 new_poly = poly_new();
562 poly_add_vertex( new_poly, original_verts[i] );
563 poly_add_vertex( new_poly, original_verts[ (i+1) % p->num_verts ] );
564 poly_add_vertex( new_poly, p->verts[ (i+1) % p->num_verts ] );
565 poly_add_vertex( new_poly, p->verts[i] );
566 mesh_polygon_add( p->mesh, new_poly );
567 result = g_slist_append( result, new_poly );
568 }
569
570 return result;
571 }
572
573
574 /* extrudes members of 'polys' such that they will remain connected.
575 * returns list of polys created, with members of 'polys' at head of list.
576 * returned list must be freed.
577 */
tools_polys_extrude_connected(Model * model,GSList * polys)578 GSList * tools_polys_extrude_connected( Model *model, GSList *polys ) {
579 GSList *result = NULL;
580 GSList *l;
581 GHashTable *verts_old2new_hash;
582 GHashTable *verts_new2old_hash;
583 Poly *p;
584 int i;
585 Poly *new_poly;
586 Vertex *new_vert;
587 Vertex *orig_vert1, *orig_vert2;
588
589 result = g_slist_copy( polys );
590
591 verts_old2new_hash = g_hash_table_new( g_direct_hash,
592 (GCompareFunc)vertices_isIdentical );
593 verts_new2old_hash = g_hash_table_new( g_direct_hash,
594 (GCompareFunc)vertices_isIdentical );
595
596 /* note that it's *probably* ok to pass in a list of polys from various
597 meshes (as opposed to polys from the *same* mesh), but I haven't
598 tested this well... */
599 for( l = polys; l != NULL; l = l->next ) {
600 p = (Poly*)l->data;
601
602 /* create n new verts, dupes of the original n */
603 /* remember them in the hash so other polys can use them too */
604 for( i = 0; i < p->num_verts; i++ ) {
605 new_vert = g_hash_table_lookup( verts_old2new_hash, p->verts[i] );
606 if( new_vert == NULL ) {
607 new_vert = vertex_dup( p->verts[i] );
608 g_hash_table_insert( verts_old2new_hash, p->verts[i], new_vert );
609 g_hash_table_insert( verts_new2old_hash, new_vert, p->verts[i] );
610 model_vertex_add( model, new_vert );
611 mesh_vertex_add( p->mesh, new_vert );
612 }
613
614 /* replace the old with the new */
615 poly_replace_vertex( p, new_vert, i );
616 }
617
618 }
619
620 for( l = polys; l != NULL; l = l->next ) {
621 p = (Poly*)l->data;
622
623 /* first, re-calc the normal, just to be safe */
624 poly_find_normal( p );
625
626 /* create n new polys */
627 for( i = 0; i < p->num_verts; i++ ) {
628 int j = (i+1) % p->num_verts;
629
630 orig_vert1 = g_hash_table_lookup( verts_new2old_hash, p->verts[i] );
631 orig_vert2 = g_hash_table_lookup( verts_new2old_hash, p->verts[j] );
632
633 /* if this edge is shared with others in polys, then skip it */
634 if( polys_count_edge_usage( polys, p->verts[i], p->verts[j]) > 1 ) {
635 continue;
636 }
637
638 new_poly = poly_new();
639 poly_add_vertex( new_poly, orig_vert1 );
640 poly_add_vertex( new_poly, orig_vert2 );
641 poly_add_vertex( new_poly, p->verts[j] );
642 poly_add_vertex( new_poly, p->verts[i] );
643 mesh_polygon_add( p->mesh, new_poly );
644 result = g_slist_append( result, new_poly );
645 }
646 }
647
648 g_hash_table_destroy( verts_old2new_hash );
649 g_hash_table_destroy( verts_new2old_hash );
650
651 return result;
652 }
653
654
tools_polys_turn_edge(Model * model,GSList * polys)655 void tools_polys_turn_edge( Model *model, GSList *polys ) {
656
657 Poly *p1, *p2;
658 Vertex *shared_v1, *shared_v2;
659 Vertex *not_shared_v1, *not_shared_v2;
660 int i, shared_edges=0;
661 TexCoord *nsv1tc, *nsv2tc;
662 float nsv1tc_u, nsv1tc_v, nsv2tc_u, nsv2tc_v;
663
664 if( polys == NULL ) return;
665 if( g_slist_length( polys ) != 2 ) {
666 printf( "Turn edge: exactly 2 polys must be selected\n" );
667 return;
668 }
669
670 p1 = (Poly*)polys->data;
671 p2 = (Poly*)polys->next->data;
672
673 /* check to make sure that all polys are triangles */
674 if( p1->num_verts != 3 || p2->num_verts != 3 ) {
675 printf( "Turn edge: both polygons must be triangles\n" );
676 return;
677 }
678
679 /* check to make sure that the two polys are in the same mesh */
680 if( p1->mesh != p2->mesh ) {
681 printf( "Turn edge: both polygons must be in the same mesh\n" );
682 return;
683 }
684
685 /* check to make sure that the two polys share exactly one edge */
686 for( i = 0; i < p1->num_verts; i++ ) {
687 if( poly_has_edge( p2,
688 p1->verts[i], p1->verts[(i+1)%p1->num_verts] ) )
689 {
690 shared_edges ++;
691 shared_v1 = p1->verts[i];
692 shared_v2 = p1->verts[(i+1)%p1->num_verts];
693 }
694 }
695 if( shared_edges != 1 ) {
696 printf( "Turn edge: polygons must be adjacent\n" );
697 return;
698 }
699
700 /* find out which two verts are *not* shared */
701 for( i = 0; i < p1->num_verts; i++ ) {
702 if( p1->verts[i] != shared_v1 && p1->verts[i] != shared_v2 )
703 not_shared_v1 = p1->verts[i];
704 }
705 for( i = 0; i < p2->num_verts; i++ ) {
706 if( p2->verts[i] != shared_v1 && p2->verts[i] != shared_v2 )
707 not_shared_v2 = p2->verts[i];
708 }
709
710 /* remember the texcoords */
711 /* remember p2's nsv2, to be used for p1 */
712 nsv2tc = poly_get_texcoord( p2, not_shared_v2 );
713 nsv2tc_u = nsv2tc->x;
714 nsv2tc_v = nsv2tc->y;
715 /* remember p1's nsv1, to be used for p2 */
716 nsv1tc = poly_get_texcoord( p1, not_shared_v1 );
717 nsv1tc_u = nsv1tc->x;
718 nsv1tc_v = nsv1tc->y;
719
720 /* perform the swap */
721 poly_replace_vertex( p1, not_shared_v2,
722 array_find( (void**)p1->verts, p1->num_verts, shared_v2 ) );
723 poly_replace_vertex( p2, not_shared_v1,
724 array_find( (void**)p2->verts, p2->num_verts, shared_v1 ) );
725
726 /* set the texcoords to the proper values */
727 texcoord_change_values( poly_get_texcoord( p1, not_shared_v2 ),
728 nsv2tc_u, nsv2tc_v );
729 texcoord_change_values( poly_get_texcoord( p2, not_shared_v1 ),
730 nsv1tc_u, nsv1tc_v );
731 }
732
733
tools_polys_make_triangles(Model * model,GSList * plist)734 void tools_polys_make_triangles( Model *model, GSList *plist ) {
735
736 GSList *l, *polys;
737 Poly *p, *newpoly;
738 int i;
739
740 if( plist == NULL ) return;
741
742 polys = g_slist_copy( plist );
743
744 for( l = polys; l; l = l->next ) {
745 p = (Poly *)l->data;
746 if( p->num_verts < 4 )
747 continue;
748
749 for( i = 2; i < p->num_verts; i++ ) {
750 newpoly = poly_new();
751 poly_add_vertex( newpoly, p->verts[0] );
752 texcoord_change_values(
753 poly_get_texcoord( newpoly, p->verts[0] ),
754 p->tc[0]->x, p->tc[0]->y );
755 poly_add_vertex( newpoly, p->verts[i-1] );
756 texcoord_change_values(
757 poly_get_texcoord( newpoly, p->verts[i-1] ),
758 p->tc[i-1]->x, p->tc[i-1]->y );
759 poly_add_vertex( newpoly, p->verts[i] );
760 texcoord_change_values(
761 poly_get_texcoord( newpoly, p->verts[i] ),
762 p->tc[i]->x, p->tc[i]->y );
763 mesh_polygon_add( p->mesh, newpoly );
764 }
765 model_polygon_remove( model, p );
766 }
767
768 g_slist_free( polys );
769 }
770
771
772 #define TOOLS_ADD_VERT_AND_CHANGE_TC( THE_VERT ) \
773 poly_add_vertex( newpoly, THE_VERT );\
774 tools_find_texcoords_for_vert( p, THE_VERT, verts_for_new_polys, &u, &v );\
775 texcoord_change_values( poly_get_texcoord( newpoly, THE_VERT ), u, v );
776 #define TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( THE_VERT ) \
777 poly_add_vertex( newpoly, THE_VERT );\
778 tools_find_texcoords_for_middlevert( p, &u, &v );\
779 texcoord_change_values( poly_get_texcoord( newpoly, THE_VERT ), u, v );
780
tools_polys_subdivide_midpoint(Model * model,GSList * plist)781 void tools_polys_subdivide_midpoint( Model *model, GSList *plist ) {
782
783 GSList *l, *ll, *polys, *nearverts, *verts_for_new_polys = NULL;
784 Poly *p, *newpoly;
785 Vertex *newvert, *middlevert;
786 int i;
787 float u, v;
788
789 if( plist == NULL ) return;
790
791 polys = g_slist_copy( plist );
792
793 for( l = polys; l; l = l->next ) {
794 p = (Poly *)l->data;
795 if( p->num_verts < 3 )
796 continue;
797
798 /* for each edge, insert a vert at the midpoint */
799 for( i = 0; i < p->num_verts; i++ ) {
800 int next_i = (i + 1) % p->num_verts;
801 verts_for_new_polys = g_slist_append( verts_for_new_polys, p->verts[i] );
802
803 newvert = vertex_new();
804
805 /* find the middle of the edge */
806 vector_add( newvert->v, p->verts[i]->v, p->verts[next_i]->v );
807 vector_mul( newvert->v, newvert->v, 1. / 2. );
808
809 nearverts = tools_find_near_verts( model->verts, newvert );
810
811 if( nearverts != NULL ) {
812 /* if an existing vert was found near newvert */
813 vertex_delete( newvert );
814 newvert = (Vertex*)nearverts->data;
815 g_slist_free( nearverts );
816 } else {
817 model_vertex_add( model, newvert );
818 }
819
820 verts_for_new_polys = g_slist_append( verts_for_new_polys, newvert );
821 }
822
823 /* if this is not a triangle, create one more vert at the middle */
824 if( p->num_verts >= 4 ) {
825 middlevert = vertex_new();
826 vertices_find_mean( verts_for_new_polys, middlevert );
827 model_vertex_add( model, middlevert );
828 }
829
830 for( ll = verts_for_new_polys; ll && ll->next; ) {
831 int first = (ll == verts_for_new_polys);
832 Vertex *firstvert;
833 newpoly = poly_new();
834
835 if( first ) {
836 /* if this is the first poly to be created in place of p, then
837 we have some special-case things to do */
838 firstvert = (Vertex*)(g_slist_last(verts_for_new_polys)->data);
839 } else {
840 firstvert = (Vertex*)ll->data;
841 ll = ll->next;
842 }
843 TOOLS_ADD_VERT_AND_CHANGE_TC( firstvert )
844
845 TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
846 ll = ll->next;
847 TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
848
849 if( p->num_verts >= 4 ) {
850 /* if this is not a triangle, the final vert in newpoly should
851 be the middlevert */
852 TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( middlevert )
853 }
854
855 mesh_polygon_add( p->mesh, newpoly );
856 }
857
858 if( p->num_verts == 3 ) {
859 /* if this is a triangle, add the final (middle) new poly */
860 newpoly = poly_new();
861 TOOLS_ADD_VERT_AND_CHANGE_TC(
862 (Vertex*)verts_for_new_polys->next->data )
863 TOOLS_ADD_VERT_AND_CHANGE_TC(
864 (Vertex*)verts_for_new_polys->next->next->next->data )
865 TOOLS_ADD_VERT_AND_CHANGE_TC(
866 (Vertex*)(g_slist_last(verts_for_new_polys)->data) )
867 mesh_polygon_add( p->mesh, newpoly );
868 }
869
870 g_slist_free( verts_for_new_polys );
871 verts_for_new_polys = NULL;
872
873 model_polygon_remove( model, p );
874 }
875
876 g_slist_free( polys );
877 }
878
879
tools_polys_subdivide_quads(Model * model,GSList * plist)880 void tools_polys_subdivide_quads( Model *model, GSList *plist ) {
881
882 GSList *l, *ll, *polys, *nearverts, *verts_for_new_polys = NULL;
883 Poly *p, *newpoly;
884 Vertex *newvert, *middlevert;
885 int i;
886 float u, v;
887
888 if( plist == NULL ) return;
889
890 polys = g_slist_copy( plist );
891
892 for( l = polys; l; l = l->next ) {
893 p = (Poly *)l->data;
894 if( p->num_verts < 3 )
895 continue;
896
897 /* for each edge, insert a vert at the midpoint */
898 for( i = 0; i < p->num_verts; i++ ) {
899 int next_i = (i + 1) % p->num_verts;
900 verts_for_new_polys = g_slist_append( verts_for_new_polys, p->verts[i] );
901
902 newvert = vertex_new();
903
904 /* find the middle of the edge */
905 vector_add( newvert->v, p->verts[i]->v, p->verts[next_i]->v );
906 vector_mul( newvert->v, newvert->v, 1. / 2. );
907
908 nearverts = tools_find_near_verts( model->verts, newvert );
909
910 if( nearverts != NULL ) {
911 /* if an existing vert was found near newvert */
912 vertex_delete( newvert );
913 newvert = (Vertex*)nearverts->data;
914 g_slist_free( nearverts );
915 } else {
916 model_vertex_add( model, newvert );
917 }
918
919 verts_for_new_polys = g_slist_append( verts_for_new_polys, newvert );
920 }
921
922 /* create one more vert at the middle */
923 middlevert = vertex_new();
924 vertices_find_mean( verts_for_new_polys, middlevert );
925 model_vertex_add( model, middlevert );
926
927 for( ll = verts_for_new_polys; ll && ll->next; ) {
928 int first = (ll == verts_for_new_polys);
929 Vertex *firstvert;
930 newpoly = poly_new();
931
932 if( first ) {
933 /* if this is the first poly to be created in place of p, then
934 we have some special-case things to do */
935 firstvert = (Vertex*)(g_slist_last(verts_for_new_polys)->data);
936 } else {
937 firstvert = (Vertex*)ll->data;
938 ll = ll->next;
939 }
940 TOOLS_ADD_VERT_AND_CHANGE_TC( firstvert )
941
942 TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
943 ll = ll->next;
944 TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
945
946 /* the fourth (final) vert in newpoly should be the middlevert */
947 TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( middlevert )
948
949 mesh_polygon_add( p->mesh, newpoly );
950 }
951
952 g_slist_free( verts_for_new_polys );
953 verts_for_new_polys = NULL;
954
955 model_polygon_remove( model, p );
956 }
957
958 g_slist_free( polys );
959 }
960
961
tools_polys_subdivide_tris(Model * model,GSList * plist)962 void tools_polys_subdivide_tris( Model *model, GSList *plist ) {
963
964 GSList *l, *polys, *verts_for_new_polys = NULL;
965 Poly *p, *newpoly;
966 Vertex *middlevert;
967 int i;
968 float u, v;
969
970 if( plist == NULL ) return;
971
972 polys = g_slist_copy( plist );
973
974 for( l = polys; l; l = l->next ) {
975 p = (Poly *)l->data;
976 if( p->num_verts < 3 )
977 continue;
978
979 /* build a list of the verts, for use by vertices_find_mean and the
980 texture coordinate stuff */
981 for( i = 0; i < p->num_verts; i++ ) {
982 verts_for_new_polys = g_slist_append( verts_for_new_polys, p->verts[i] );
983 }
984
985 /* create one vert at the middle */
986 middlevert = vertex_new();
987 vertices_find_mean( verts_for_new_polys, middlevert );
988 model_vertex_add( model, middlevert );
989
990 /* for each edge, create a triangle using the two verts on the edge
991 and the middle vert */
992 for( i = 0; i < p->num_verts; i++ ) {
993 int next_i = (i + 1) % p->num_verts;
994 newpoly = poly_new();
995
996 TOOLS_ADD_VERT_AND_CHANGE_TC( p->verts[i] )
997 TOOLS_ADD_VERT_AND_CHANGE_TC( p->verts[next_i] )
998 TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( middlevert )
999
1000 mesh_polygon_add( p->mesh, newpoly );
1001 }
1002
1003 g_slist_free( verts_for_new_polys );
1004 verts_for_new_polys = NULL;
1005
1006 model_polygon_remove( model, p );
1007 }
1008
1009 g_slist_free( polys );
1010 }
1011
1012
tools_find_texcoords_for_vert(Poly * p,Vertex * vert,GSList * verts,float * u,float * v)1013 void tools_find_texcoords_for_vert( Poly *p, Vertex *vert, GSList *verts,
1014 float *u, float *v )
1015 {
1016 TexCoord *tc, *nexttc, *prevtc;
1017 GSList *listelem, *nextelem, *prevelem;
1018 int elem_index;
1019
1020 // fixme - check for NULL
1021
1022 *u = 0.;
1023 *v = 0.;
1024
1025 /* if the vert is already in the poly, we can retrieve its
1026 texcoord vals directly */
1027 tc = poly_get_texcoord( p, vert );
1028 if( tc ) {
1029 *u = tc->x;
1030 *v = tc->y;
1031 return;
1032 }
1033
1034 /* if it is not in the poly, find its location in the list, as well as
1035 the next and previous verts in the list */
1036 listelem = g_slist_find( verts, vert );
1037 if( listelem == NULL )
1038 return;
1039
1040 nextelem = listelem->next;
1041 if( nextelem == NULL )
1042 /* loop around to beginning of list */
1043 nextelem = verts;
1044
1045 elem_index = g_slist_position( verts, listelem );
1046 if( elem_index == 0 )
1047 /* loop around to end of list */
1048 elem_index = g_slist_length( verts ) - 1;
1049 else
1050 elem_index--;
1051 prevelem = g_slist_nth( verts, elem_index );
1052
1053 /* Because of the way in which the verts list is set up (see the
1054 tools_polys_subdivide funcs) the verts in the list alternate between
1055 new and existing vertices. As such, since we know that 'vert' is a new
1056 vert, the verts before and after v in the list *should* exist in poly p,
1057 and hence have texture coordinates. We will use those texture coordinates
1058 to calculate the coords for 'vert'. */
1059
1060 nexttc = poly_get_texcoord( p, (Vertex*)nextelem->data );
1061 prevtc = poly_get_texcoord( p, (Vertex*)prevelem->data );
1062
1063 if( nexttc == NULL || prevtc == NULL ) return;
1064
1065 *u = (nexttc->x + prevtc->x) / 2.;
1066 *v = (nexttc->y + prevtc->y) / 2.;
1067
1068 }
1069
1070
tools_find_texcoords_for_middlevert(Poly * p,float * u,float * v)1071 void tools_find_texcoords_for_middlevert( Poly *p, float *u, float *v )
1072 {
1073 int i;
1074 *u = 0.;
1075 *v = 0.;
1076 for( i = 0; i < p->num_verts; i++ ) {
1077 *u += p->tc[i]->x;
1078 *v += p->tc[i]->y;
1079 }
1080 *u /= (float)p->num_verts;
1081 *v /= (float)p->num_verts;
1082 }
1083
1084
tools_polys_smooth(Model * model,GSList * polys,float factor)1085 void tools_polys_smooth( Model *model, GSList *polys, float factor ) {
1086
1087 GSList *vertlist, *l;
1088 GSList *adjacent_polys, *adjacent_verts;
1089 GSList *templist;
1090 GHashTable *verthash;
1091 Vertex *v, avgVert;
1092 float *tempPt, delta[3];
1093
1094 /* verthash will hold the new locations for the vertices */
1095
1096 vertlist = polys_get_verts_unique( polys );
1097 verthash = g_hash_table_new_full( NULL, NULL, NULL, g_free );
1098 /* populate verthash */
1099 for( l = vertlist; l; l = l->next ) {
1100 v = (Vertex *)l->data;
1101 tempPt = g_malloc( sizeof( float ) * 3 );
1102 vector_copy( tempPt, v->v );
1103 g_hash_table_insert( verthash, v, tempPt );
1104 }
1105
1106 /* for each vert, record its new position in the hash table */
1107 for( l = vertlist; l; l = l->next ) {
1108 v = (Vertex *)l->data;
1109 tempPt = g_hash_table_lookup( verthash, v );
1110
1111 /* for each vert, find all verts that are adjacent to it */
1112 templist = g_slist_append( NULL, v );
1113 adjacent_polys = verts_get_polys_unique( templist );
1114 g_slist_free( templist );
1115 adjacent_verts = polys_get_verts_unique( adjacent_polys );
1116 /* v will be in adjacent_verts, but that's OK */
1117
1118 /* using the list of adjacent verts, find their average */
1119 vertices_find_mean( adjacent_verts, &avgVert );
1120
1121 /* move the vert closer to the average */
1122 vector_sub( delta, avgVert.v, v->v );
1123 vector_mul( delta, delta, factor );
1124 vector_add( tempPt, tempPt, delta );
1125
1126 g_slist_free( adjacent_polys );
1127 g_slist_free( adjacent_verts );
1128 }
1129
1130 /* now move all of the verts */
1131 for( l = vertlist; l; l = l->next ) {
1132 v = (Vertex *)l->data;
1133 tempPt = g_hash_table_lookup( verthash, v );
1134 vector_copy( v->v, tempPt );
1135 }
1136
1137 g_slist_free( vertlist );
1138 g_hash_table_destroy( verthash );
1139
1140 action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
1141 }
1142
1143 /* any more edges than this and it doesn't count as a hole */
1144 #define TOOLS_HOLE_THRESHOLD 4
1145
tools_polys_find_holes(Model * model,GSList * polys)1146 void tools_polys_find_holes( Model *model, GSList *polys ) {
1147 /* Finds holes and 'T' junctions in the list of polys.
1148 Any group of "outside edges" consisting of fewer than a certain number
1149 of edges will be tagged as a hole. Note that, because of this and because
1150 of the way in which outside edges are grouped, it currently can not
1151 detect "figure-eight" type holes (ie two holes that share a vertex). */
1152
1153 /* Here's how this *should* have been implemented:
1154 - find the outside edges
1155 - construct a graph of the edges and verts
1156 - form 'holes' by
1157 - performing shortest-path on the graph
1158 - once shortest-loop-from-an-edge-back-to-itself is found,
1159 remove involved edges from graph and form a vertlist. This is
1160 one hole.
1161 - form holes until there are no edges left in graph, or (error state)
1162 the remaining edges don't form any loops
1163 - any holes smaller than a certain threshold can be selected,
1164 filled-in by poly, etc.
1165 */
1166
1167 GSList *l, *ll;
1168 GSList *outside_edges;
1169 GSList *holes = NULL;
1170 GSList *bins = NULL, *bins_copy;
1171 GSList *bin, *bl;
1172 int bin_is_part_of_another_bin;
1173
1174 outside_edges = tools_find_outside_edges( polys );
1175
1176 /* sort the edges into bins */
1177 for( l = outside_edges; l; l = l->next ) {
1178 tools_sort_edge_into_bins( &bins, (Edge*)l->data );
1179 }
1180
1181 /* now we must consolidate the bins as much as possible */
1182 /* For every bin in 'bins', remove it from bins
1183 and try adding its contents back into 'bins'. The idea is that, if the
1184 smaller bin should actually be part of a larger bin, some of its contents
1185 will be added to that larger bin and the smaller bin (if it re-forms at
1186 all) will have fewer edges. However, if the smaller bin *is* its own,
1187 independent bin, it will be completely re-formed as its contents are
1188 added back into 'bins'. If we repeat this process until there are no bins
1189 that are part of other bins, we will have consolidated all of the bins
1190 as much as possible. */
1191 do {
1192 bin_is_part_of_another_bin = FALSE;
1193 bins_copy = g_slist_copy( bins );
1194 for( bin = bins_copy; bin; bin = bin->next ) {
1195 GSList *binlist, *btemp, *bin_that_first_edge_was_added_to = NULL;
1196 /* remove bin from bins */
1197 binlist = bin->data;
1198 bins = g_slist_remove( bins, binlist );
1199
1200 for( bl = binlist; bl; bl = bl->next ) {
1201 btemp = tools_sort_edge_into_bins( &bins, (Edge*)bl->data );
1202 if( bin_that_first_edge_was_added_to == NULL ) {
1203 bin_that_first_edge_was_added_to = btemp;
1204 if( g_slist_length( bin_that_first_edge_was_added_to->data ) > 1 )
1205 /* bin is part of another bin */
1206 bin_is_part_of_another_bin = TRUE;
1207 }
1208 if( bin_that_first_edge_was_added_to != btemp ) {
1209 /* bin is part of another bin */
1210 bin_is_part_of_another_bin = TRUE;
1211 }
1212 }
1213 g_slist_free( binlist );
1214 }
1215 g_slist_free( bins_copy );
1216 } while( bin_is_part_of_another_bin );
1217
1218 /* Now traverse 'bins', and for each bin shorter than the threshold add
1219 its contents to the holes list. Finally, free the bin. */
1220 for( bin = bins; bin; bin = bin->next ) {
1221 if( !(g_slist_length( bin->data ) > TOOLS_HOLE_THRESHOLD) ) {
1222 /* holes smaller than the TOOLS_HOLE_THRESHOLD are assumed
1223 not to be figure-8 holes, and can be converted directly into
1224 a vertlist */
1225 holes = g_slist_append( holes,
1226 tools_convert_edgelist_to_vertlist( bin->data ) );
1227 } else {
1228 /* the edgelist may or may not be a figure-8 hole. we should
1229 determine if it is a figure-8, and if so attempt to subdivide it. */
1230 if( tools_edgelist_can_be_subdivided( bin->data ) ) {
1231 printf( "Found a figure-eight hole, with %i edges\n", g_slist_length( bin->data ) );
1232 }
1233 }
1234
1235 g_slist_free( bin->data );
1236 bin->data = NULL;
1237 }
1238 g_slist_free( bins );
1239
1240 /* free edges */
1241 for( l = outside_edges; l; l = l->next ) {
1242 free( l->data );
1243 }
1244 g_slist_free( outside_edges );
1245
1246 /* We now have a list of lists of verts. Each list of verts represents
1247 a 'hole' in the poly-list surface. */
1248
1249 /*print the lists, select the verts*/
1250 sel_vert_unselect_all( model );
1251 printf( "%s : found %i holes\n", __FUNCTION__, g_slist_length( holes ) );
1252 for( l = holes; l; l = l->next ) {
1253 printf( "\tverts: " );
1254 sel_vert_select_list( l->data );
1255 for( ll = l->data; ll; ll = ll->next ) {
1256 printf( "%x ", (unsigned int)ll->data );
1257 }
1258 printf( "\n" );
1259 }
1260 }
1261
1262
tools_convert_edgelist_to_vertlist(GSList * edges)1263 GSList * tools_convert_edgelist_to_vertlist( GSList *edges ) {
1264
1265 GSList *l, *verts = NULL;
1266 Edge *e, *prev = NULL;
1267 int num_edges = g_slist_length( edges );
1268
1269 while( g_slist_length(verts) < num_edges ) {
1270 for( l = edges; l; l = l->next ) {
1271 e = (Edge *)l->data;
1272 if( prev == NULL )
1273 {
1274 verts = g_slist_append( verts, e->v1 );
1275 verts = g_slist_append( verts, e->v2 );
1276 e->flag = TRUE;
1277 prev = e;
1278 }
1279 else if( !(e->flag) &&
1280 tools_edges_areConnected( e, prev ) &&
1281 tools_edges_isNotEqual( e, prev ) )
1282 {
1283 Vertex *v = NULL;
1284 /* now figure out which vert should be next */
1285 if( e->v1 == prev->v1 || e->v1 == prev->v2 )
1286 v = e->v2;
1287 else if( e->v2 == prev->v1 || e->v1 == prev->v2 )
1288 v = e->v1;
1289
1290 if( v && g_slist_find( verts, v ) == NULL )
1291 verts = g_slist_append( verts, v );
1292 e->flag = TRUE;
1293 prev = e;
1294 }
1295 }
1296 }
1297
1298 return verts;
1299 }
1300
1301
tools_edgelist_can_be_subdivided(GSList * edges)1302 int tools_edgelist_can_be_subdivided( GSList *edges ) {
1303 /* */
1304 int figure_eight = FALSE;
1305 int well_behaved = TRUE;
1306 GSList *l, *ll;
1307 Edge *e;
1308 Vertex *v;
1309 int edges_with_v;
1310
1311 for( l = edges; l; l = l->next ) {
1312 e = (Edge *)l->data;
1313 v = e->v1;
1314 while( v ) {
1315 edges_with_v = 0;
1316 for( ll = edges; ll; ll = ll->next ) {
1317 if( tools_edge_hasVertex( (Edge*)ll->data, v ) )
1318 edges_with_v++;
1319 }
1320 if( edges_with_v > 2 )
1321 figure_eight = TRUE;
1322 if( edges_with_v % 2 )
1323 well_behaved = FALSE;
1324 v = (v==e->v1) ? e->v2 : NULL;
1325 }
1326 }
1327
1328 return( figure_eight && well_behaved );
1329 }
1330
1331
tools_sort_edge_into_bins(GSList ** bins,Edge * edge)1332 GSList * tools_sort_edge_into_bins( GSList **bins, Edge *edge ) {
1333 /* returns the bin that edge was placed in */
1334
1335 GSList *l, *ll;
1336 GSList *destination_bin = NULL;
1337 int largest_matching_bin_size = -1;
1338
1339 /* look for a proper bin to put edge into. It is important that, if an
1340 edge could go in one of several bins, we add the edge to the largest
1341 bin. */
1342 for( l = *bins; l; l = l->next ) {
1343 int bin_size = g_slist_length( l->data );
1344 for( ll = l->data; ll; ll = ll->next ) {
1345 if( tools_edges_areConnected( edge, (Edge*)ll->data ) &&
1346 bin_size >= largest_matching_bin_size ) {
1347 destination_bin = l;
1348 largest_matching_bin_size = bin_size;
1349 }
1350 }
1351 }
1352
1353 /* if couldn't find a proper bin, create one and add it to bins */
1354 if( destination_bin == NULL ) {
1355 *bins = g_slist_append( *bins, NULL );
1356 destination_bin = g_slist_last( *bins );
1357 destination_bin->data = NULL;
1358 }
1359
1360 /* append edge to destination_bin */
1361 destination_bin->data = g_slist_append( destination_bin->data, edge );
1362
1363 return destination_bin;
1364 }
1365
1366
tools_find_outside_edges(GSList * polys)1367 GSList *tools_find_outside_edges( GSList *polys ) {
1368 GSList *edges = NULL;
1369 GSList *l;
1370 Poly *p;
1371 Edge *e;
1372 int i, next_i;
1373
1374 for( l = polys; l; l = l->next ) {
1375 p = (Poly *)l->data;
1376
1377 for( i = 0; i < p->num_verts; i++ ) {
1378 next_i = (i+1) % p->num_verts;
1379
1380 /* this edge is not an outside edge if it is shared with others
1381 in 'polys' */
1382 if( polys_count_edge_usage( polys,
1383 p->verts[i], p->verts[next_i]) > 1 )
1384 continue;
1385
1386 /* create an Edge for this edge */
1387 e = (Edge *)malloc( sizeof( Edge ) );
1388 memset( e, '\0', sizeof( Edge ) );
1389 e->v1 = p->verts[i];
1390 e->v2 = p->verts[next_i];
1391 /* check to see if it is already in the list */
1392 if( g_slist_find_custom( edges, e,
1393 (GCompareFunc)tools_edges_isNotEqual ) )
1394 {
1395 free( e );
1396 continue;
1397 }
1398 /* and add it to the list */
1399 edges = g_slist_prepend( edges, e );
1400 }
1401 }
1402
1403 return edges;
1404 }
1405
1406
tools_edges_isNotEqual(Edge * e1,Edge * e2)1407 gboolean tools_edges_isNotEqual( Edge *e1, Edge *e2 ) {
1408 int equal = FALSE;
1409
1410 if( e1->v1 == e2->v1 && e1->v2 == e2->v2 )
1411 equal = TRUE;
1412 if( e1->v2 == e2->v1 && e1->v1 == e2->v2 )
1413 equal = TRUE;
1414
1415 return !equal;
1416 }
1417
1418
tools_edges_areConnected(Edge * e1,Edge * e2)1419 gboolean tools_edges_areConnected( Edge *e1, Edge *e2 ) {
1420 int connected = FALSE;
1421
1422 if( e1->v1 == e2->v1 || e1->v2 == e2->v2 ||
1423 e1->v2 == e2->v1 || e1->v1 == e2->v2 )
1424 connected = TRUE;
1425
1426 return connected;
1427 }
1428
1429
tools_edge_hasVertex(Edge * e,Vertex * v)1430 gboolean tools_edge_hasVertex( Edge *e, Vertex *v ) {
1431 int result = FALSE;
1432
1433 if( e->v1 == v || e->v2 == v )
1434 result = TRUE;
1435
1436 return result;
1437 }
1438
1439
1440
1441 /* MISC TOOLS ***********************************************************/
1442
tools_scale_along_normals(Model * model,GSList * polys,GSList * verts,float factor)1443 void tools_scale_along_normals( Model *model, GSList *polys, GSList *verts, float factor ) {
1444
1445 /* Note: this will likely *never* be undo-able. The polygon thin/fatten
1446 seems to behave predictably, but vertex thin/fatten tends to cause the
1447 vertices to start "wandering" after a few manipulations. These errors
1448 will get very bad, very quickly. As such, the application of this tool,
1449 followed by an application of this tool with a negated 'factor', does not
1450 necessarily return the model to its original state. This makes it very
1451 impractical to implement undo-ability for this tool. */
1452
1453 GSList *l, *list_to_free = NULL;
1454 Vertex *v;
1455 int i;
1456 float avg_normal[3], temp[3], len;
1457
1458 if( polys == NULL && verts == NULL )
1459 return;
1460
1461 if( verts == NULL ) {
1462 verts = polys_get_verts_unique( polys );
1463 list_to_free = verts;
1464 } else if( polys == NULL ) {
1465 polys = verts_get_polys_unique( verts );
1466 list_to_free = polys;
1467 }
1468
1469 /* have all polys update their normal */
1470 for( l = polys; l; l = l->next ) {
1471 poly_find_normal( (Poly *)l->data );
1472 }
1473
1474 /* for each vert... */
1475 for( l = verts; l != NULL; l = l->next ) {
1476 v = (Vertex*)l->data;
1477
1478 if( v->num_polys == 0 )
1479 /* should never happen, but... */
1480 continue;
1481
1482 vector_zero( avg_normal );
1483
1484 /* find the average normal, over all polys using this vert */
1485 for( i = 0; i < v->num_polys; i++ ) {
1486 vector_add( avg_normal, avg_normal, v->polys[i]->normal );
1487 }
1488 vector_mul( avg_normal, avg_normal, 1. / (float)v->num_polys );
1489
1490 /* normalize the avg_normal vector, also make sure it wasn't
1491 zero-length before it was normalized (which would indicate
1492 that something weird happened, like co-planar polys with normals
1493 pointing in opposite directions, etc) */
1494 len = vector_normalize( avg_normal );
1495 if( len < 0.01 )
1496 continue;
1497
1498 /* scale vertex in direction avg_normal is pointing */
1499 vector_find_point( temp, v->v, avg_normal, factor );
1500 vector_copy( v->v, temp );
1501 }
1502
1503 action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
1504
1505 g_slist_free( list_to_free );
1506 }
1507
1508
1509
1510
1511
1512