Welcome to mirror list, hosted at ThFree Co, Russian Federation.

BME_weld.c « tools « bmesh « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a17c07addbc8871fa5cae5180b266bc19f95a07a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*
 * BME_WELD.C
 *
 * This file contains functions for welding
 * elements in a mesh togather (remove doubles,
 * collapse, ect).
 *
 * TODO:
 *  -Rewrite this to fit into the new API
 *  -Seperate out find doubles code and put it in 
 *   BME_queries.c
 *
*/


/********* qsort routines *********/


typedef struct xvertsort {
	float x;
	BMVert *v1;
} xvertsort;

static int vergxco(const void *v1, const void *v2)
{
	const xvertsort *x1=v1, *x2=v2;

	if( x1->x > x2->x ) return 1;
	else if( x1->x < x2->x) return -1;
	return 0;
}

struct facesort {
	unsigned long x;
	struct BMFace *f;
};


static int vergface(const void *v1, const void *v2)
{
	const struct facesort *x1=v1, *x2=v2;
	
	if( x1->x > x2->x ) return 1;
	else if( x1->x < x2->x) return -1;
	return 0;
}



/*break this into two functions.... 'find doubles' and 'remove doubles'?*/

static void BME_remove_doubles__splitface(BME_Mesh *bm,BMFace *f,GHash *vhash){
	BMVert *doub=NULL, *target=NULL;
	BME_Loop *l;
	BMFace *f2=NULL;
	int split=0;
	
	l=f->loopbase;
	do{
		if(l->v->tflag1 == 2){
			target = BLI_ghash_lookup(vhash,l->v);
			if((BME_vert_in_face(target,f)) && (target != l->next->v) && (target != l->prev->v)){
				doub = l->v;
				split = 1;
				break;
			}
		}
		
		l= l->next;
	}while(l!= f->loopbase);

	if(split){
		f2 = BME_SFME(bm,f,doub,target,NULL);
		BME_remove_doubles__splitface(bm,f,vhash);
		BME_remove_doubles__splitface(bm,f2,vhash);
	}
}

int BME_remove_doubles(BME_Mesh *bm, float limit)		
{

	/* all verts with (flag & 'flag') are being evaluated */
	BMVert *v, *v2, *target;
	BMEdge *e, **edar, *ne;
	BME_Loop *l;
	BMFace *f, *nf;
	xvertsort *sortblock, *sb, *sb1;
	struct GHash *vhash;
	struct facesort *fsortblock, *vsb, *vsb1;
	int a, b, test, amount=0, found;
	float dist;

	/*Build a hash table of doubles to thier target vert/edge.*/
	vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);	
	
	/*count amount of selected vertices*/
	for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
		if(BME_SELECTED(v))amount++;
	}
	
	/*qsort vertices based upon average of coordinate. We test this way first.*/
	sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub");

	for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
		if(BME_SELECTED(v)){
			sb->x = v->co[0]+v->co[1]+v->co[2];
			sb->v1 = v;
			sb++;
		}
	}
	qsort(sortblock, amount, sizeof(xvertsort), vergxco);

	/* test for doubles */
	sb= sortblock;
	for(a=0; a<amount; a++) {
		v= sb->v1;
		if(!(v->tflag1)) { //have we tested yet?
			sb1= sb+1;
			for(b=a+1; b<amount; b++) {
				/* first test: simple distance. Simple way to discard*/
				dist= sb1->x - sb->x;
				if(dist > limit) break;
				
				/* second test: have we already done this vertex?
				(eh this should be swapped, simple equality test should be cheaper than math above... small savings 
				though) */
				v2= sb1->v1;
				if(!(v2->tflag1)) {
					dist= (float)fabs(v2->co[0]-v->co[0]);
					if(dist<=limit) {
						dist= (float)fabs(v2->co[1]-v->co[1]);
						if(dist<=limit) {
							dist= (float)fabs(v2->co[2]-v->co[2]);
							if(dist<=limit) {
								/*v2 is a double of v. We want to throw out v1 and relink everything to v*/
								BLI_ghash_insert(vhash,v2, v);
								v->tflag1 = 1; //mark this vertex as a target
								v->tflag2++; //increase user count for this vert.
								v2->tflag1 = 2; //mark this vertex as a double.
								BME_VISIT(v2); //mark for delete
							}
						}
					}
				}
				sb1++;
			}
		}
		sb++;
	}
	MEM_freeN(sortblock);

	
	/*todo... figure out what this is for...
	for(eve = em->verts.first; eve; eve=eve->next)
		if((eve->f & flag) && (eve->f & 128))
			EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f);
	*/
	
	/*We cannot collapse a vertex onto another vertex if they share a face and are not connected via a collapsable edge.
		so to deal with this we simply find these offending vertices and split the faces. Note that this is not optimal, but works.
	*/
	
	
	for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){
		if(!(BME_NEWELEM(f))){
			BME_remove_doubles__splitface(bm,f,vhash);
		}
	}
	
	for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){
		/*If either vertices of this edge are a double, we must mark it for removal and we create a new one.*/
		if(e->v1->tflag1 == 2 || e->v2->tflag1 == 2){ 
			v = v2 = NULL;
			/*For each vertex in the edge, test to find out what it should equal now.*/
			if(e->v1->tflag1 == 2) v= BLI_ghash_lookup(vhash,e->v1);
			else v = e->v1;
			if(e->v2->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,e->v2);
			else v2 = e->v2;
			
			/*small optimization, test to see if the edge needs to be rebuilt at all*/
			if((e->v1 != v) || (e->v2 != v2)){ /*will this always be true of collapsed edges?*/
				if(v == v2) e->tflag1 = 2; /*mark as a collapsed edge*/
				else if(!BME_disk_existedge(v,v2)) ne = BME_ME(bm,v,v2);
				BME_VISIT(e); /*mark for delete*/
			}
		}
	}


	/*need to remove double edges as well. To do this we decide on one edge to keep, and if its inserted into hash then we need to remove all other
	edges incident upon and relink.*/
	/*
	*	REBUILD FACES
	*
	*	Loop through double faces and if they have vertices that have been flagged, they need to be rebuilt.
	*	We do this by looking up the face 
	*rebuild faces. loop through original face, for each loop, if the edge it is attached to is marked for delete and has no 
	*other edge in the hash edge, then we know to skip that loop on face recreation. Simple.
	*/
	
	/*1st loop through, just marking elements*/
	for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ //insert bit here about double edges, mark with a flag (e->tflag2) so that we can nuke it later.
		l = f->loopbase;
		do{
			if(l->v->tflag1 == 2) f->tflag1 = 1; //double, mark for rebuild
			if(l->e->tflag1 != 2) f->tflag2++; //count number of edges in the new face.
			l=l->next;
		}while(l!=f->loopbase);
	}
	
	/*now go through and create new faces*/
	for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){
		if(f->tflag1 && f->tflag2 < 3) BME_VISIT(f); //mark for delete
		else if (f->tflag1 == 1){ /*is the face marked for rebuild*/
			edar = MEM_callocN(sizeof(BMEdge *)*f->tflag2,"Remove doubles face creation array.");
			a=0;
			l = f->loopbase;
			do{
				v = l->v;
				v2 = l->next->v;
				if(l->v->tflag1 == 2) v = BLI_ghash_lookup(vhash,l->v);
				if(l->next->v->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,l->next->v);
				ne = BME_disk_existedge(v,v2); //use BME_disk_next_edgeflag here or something to find the edge that is marked as 'target'.
				//add in call here to edge doubles hash array... then bobs your uncle.
				if(ne){
					edar[a] = ne;
					a++;
				}
				l=l->next;
			}while(l!=f->loopbase);
			
			if(BME_vert_in_edge(edar[1],edar[0]->v2)){ 
				v = edar[0]->v1; 
				v2 = edar[0]->v2;
			}
			else{ 
				v = edar[0]->v2; 
				v2 = edar[0]->v1;
			}
			
			nf = BME_MF(bm,v,v2,edar,f->tflag2);
	
			/*copy per loop data here*/
			if(nf){
				BME_VISIT(f); //mark for delete
			}
			MEM_freeN(edar);
		}
	}
	
	/*count amount of removed vert doubles*/
	a = 0;
	for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
		if(v->tflag1 == 2) a++;
	}
	
	/*free memory and return amount removed*/
	remove_tagged_polys(bm);
	remove_tagged_edges(bm);
	remove_tagged_verts(bm);
	BLI_ghash_free(vhash,NULL, NULL);
	BME_selectmode_flush(bm);
	return a;
}

static void BME_MeshWalk__collapsefunc(void *userData, BMEdge *applyedge){
	int index;
	GHash *collected = userData;
	index = BLI_ghash_size(collected);
	if(!BLI_ghash_lookup(collected,applyedge->v1)){ 
		BLI_ghash_insert(collected,index,applyedge->v1);
		index++;
	}
	if(!BLI_ghash_lookup(collected,applyedge->v2)){
		BLI_ghash_insert(collected,index,applyedge->v2);
	}
}

void BME_collapse_edges(BME_Mesh *bm){

	BMVert *v, *cvert;
	GHash *collected;
	float min[3], max[3], cent[3];
	int size, i=0, j, num=0;
	
	for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
		if(!(BME_ISVISITED(v)) && v->edge){
			/*initiate hash table*/
			collected = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
			/*do the walking.*/
			BME_MeshWalk(bm,v,BME_MeshWalk__collapsefunc,collected,BME_RESTRICTSELECT);
			/*now loop through the hash table twice, once to calculate bounding box, second time to do the actual collapse*/
			size = BLI_ghash_size(collected);
			/*initial values*/
			VECCOPY(min,v->co);
			VECCOPY(max,v->co);
			cent[0] = cent[1] = cent[2]=0;
			for(i=0; i<size; i++){
				cvert = BLI_ghash_lookup(collected,i);
				cent[0] = cent[0] + cvert->co[0];
				cent[1] = cent[1] + cvert->co[1];
				cent[2] = cent[2] + cvert->co[2];
			}
			
			cent[0] = cent[0] / size;
			cent[1] = cent[1] / size;
			cent[2] = cent[2] / size;
			
			for(i=0; i<size; i++){
				cvert = BLI_ghash_lookup(collected,i);
				VECCOPY(cvert->co,cent);
				num++;
			}
			/*free the hash table*/
			BLI_ghash_free(collected,NULL, NULL);
		}
	}
	/*if any collapsed, call remove doubles*/
	if(num){
		//need to change selection mode here, OR do something else? Or does tool change selection mode?
		//selectgrep
		//first clear flags
		BMEdge *e;
		BMFace *f;
		BME_clear_flag_all(bm,BME_VISITED);
		for(v=BME_first(bm,BME_VERT); v; v=BME_next(bm,BME_VERT,v)) v->tflag1 = v->tflag2 = 0;
		for(e=BME_first(bm,BME_EDGE); e; e=BME_next(bm,BME_EDGE,e)) e->tflag1 = e->tflag2 = 0;
		for(f=BME_first(bm,BME_POLY); f; f=BME_next(bm,BME_POLY,f)) f->tflag1 = f->tflag2 = 0;
		/*now call remove doubles*/
		BME_remove_doubles(bm,0.0000001);
	}
	BME_selectmode_flush(bm);
}