1/*
2<?xml version='1.0' standalone='yes' ?>
3<!--  xml header for scripts & plugin manager -->
4<script>
5	<name>Join Objects</name>
6	<author>Julian MacDonald</author>
7	<version>1.8</version>
8    <beta>2</beta>
9	<date>23/08/2007</date>
10	<description>
11This script joins a set of selected objects into a single triangle mesh
12non-triangle meshes are converted to triangle meshes using the user-entered Max Error.
13Smoothness values for vertices and edges are retained.  There is also the ability to 'weld' vertices
14within a certain distance of each other.
15Textures are also partly retained.
16Still to do:  	support for layered textures
17Problems:	texture scaling/orientation/position not retained correctly (need access to methods - Peter to allow in next version)
18		if a texture is used more than once with different mappings, only the last mapping is transferred.
19    </description>
20    <comments>
21    </comments>
22</script>
23*/
24
25
26scene=window.getScene();
27sel=scene.getSelection();
28//
29// make sure at least 2 objects have been selected
30if (sel.length<2)
31{
32	new MessageDialog(window,"Select at least 2 objects");
33	return;
34}
35//
36ObjInfo=new ObjectInfo[sel.length];   // array for holding ObjInfos for each selected object
37//
38//
39//  get a name for the new object and tolerance for
40// triangle mesh conversion if required
41//
42nameField=new BTextField("JoinedMesh",20);
43tolField=new ValueField(0.02,ValueField.POSITIVE);
44yesWeld = new BCheckBox();
45weldTolField=new ValueField(0.01,ValueField.POSITIVE);
46yesTex=new BCheckBox();
47sp1=new Spacer(nameField,nameField);
48sp2=new Spacer(nameField,nameField);
49sp3=new Spacer(nameField,nameField);
50//
51widgets=new Widget[]{nameField,sp1,tolField,sp2,yesWeld,weldTolField,sp3,yesTex};
52labels=new String[]{"Name:",null,"Surface Accuracy:",null,"Weld Close Points:","Weld Distance:",null,"Retain Textures:"};
53dlg = new ComponentsDialog(window, "Options for Resulting Mesh:",widgets,labels);
54if (!dlg.clickedOk()) return;
55//
56objname = nameField.getText();
57tol=tolField.getValue();
58wantsWeld=yesWeld.getState();
59weldTol=weldTolField.getValue();
60wantsTex=yesTex.getState();
61//
62// go through selected objects and get ObjInfos
63// convert any non-triangle meshes to triangle meshes
64//
65count=0;
66for(int i=0;i<sel.length;i++)
67{
68	selObjInfo=scene.getObject(sel[i]);  // get the ith ObjInfo
69	if (selObjInfo.object instanceof TriangleMesh)   // if it's a triangle mesh get it
70	{
71		ObjInfo[i]=scene.getObject(sel[i]);
72	}
73	else  // otherwise convert to a triangle mesh
74	{
75		Obj=selObjInfo.object.convertToTriangleMesh(tol);
76		ObjInfo[i]=new ObjectInfo(Obj,selObjInfo.coords,"");
77	}
78}
79//
80//
81// get number of vertices/faces in joined mesh
82vtot=0;
83ftot=0;
84etot=0;
85numV=new int[sel.length];
86for (i=0;i<sel.length;i++)
87{
88	v=ObjInfo[i].object.getVertices();
89	f=ObjInfo[i].object.getFaces();
90	e=ObjInfo[i].object.getEdges();
91	numV[i]=v.length;  // number of vertices in each object
92	vtot=vtot+v.length;   // total number of vertices in resulting mesh
93	ftot=ftot+f.length;  // total number of faces in resulting mesh
94	etot=etot+e.length;
95}
96//
97va=new Vec3[vtot];   // create an array to hold the new mesh vertex coordinates
98vs=new float[vtot];  // create an array to hold the new mesh vertex Smoothness values
99es=new float[etot];  // create an array to hold the new mesh edge Smoothness values
100fc=new int[ftot][3];  // create an array to hold the new mesh face data
101count=0;
102ecount=0;
103fcount=0;
104disp=0;
105// cycle through the selected objects
106for (i=0;i<sel.length;i++)
107{
108	// get vertex information and put into array for new mesh
109	v=ObjInfo[i].object.getVertices();
110	mv=ObjInfo[i].coords.fromLocal();
111	for (j=0;j<v.length;j++)
112	{
113		newV=new Vec3(v[j].r);
114		mv.transform(newV);
115		va[count]=newV;
116		vs[count]=v[j].smoothness;
117		count++;
118	}
119	// get edge smoothness values and put into array for new mesh
120	e=ObjInfo[i].object.getEdges();
121	for (k=0;k<e.length;k++)
122	{
123		es[ecount]=e[k].smoothness;
124		ecount++;
125	}
126	 // get face information and put into array for new mesh
127	f=ObjInfo[i].object.getFaces();
128	for (j=0;j<f.length;j++)
129	{
130		fc[fcount][0]=f[j].v1+disp;
131		fc[fcount][1]=f[j].v2+disp;
132		fc[fcount][2]=f[j].v3+disp;
133		fcount++;
134	}
135	disp=disp+numV[i];
136}
137//
138//
139// add the new object to the scene
140newMesh=new TriangleMesh(va,fc);
141// put original smoothness values back
142newVerts=newMesh.getVertices();
143for (i=0;i<newVerts.length;i++)
144{
145	newVerts[i].smoothness=vs[i];
146}
147//
148newEdges=newMesh.getEdges();
149for (i=0;i<newEdges.length;i++)
150{
151	newEdges[i].smoothness=es[i];
152}
153//
154//
155//
156//   TEXTURES
157if (wantsTex)
158{
159// ** Transfer the texture information from the original objects to the new mesh **
160//
161layTex=new LayeredTexture(newMesh);  // create a new LayeredTexture
162layMap=new LayeredMapping(newMesh,layTex); // get the associated LayeredMapping
163//
164tex=new Texture[sel.length];  // array for textures of each object
165texMap=new TextureMapping[sel.length];  // array for texture mappings for each object texture
166//
167// loop through selected objects (and hence individual textures) and get the texture details
168fc=0;  // face count
169vc=0; // vertex count
170for (i=0;i<sel.length;i++)
171{
172	tex[i]=ObjInfo[i].object.getTexture();    // get the texture for the ith object
173	texMap[i]=ObjInfo[i].object.getTextureMapping().duplicate();  // get the mapping (i.e. type and scaling, orientation etc.)
174	layMap.addLayer(tex[i]);  // add the ith object's texture to the layered texture
175	layMap.setLayerMode(i,0);
176}
177newMesh.setTexture(layTex,layMap);   // apply the layered texture to the new mesh
178//
179//  The basic method here is to assign each layer of the new mesh's layered texture per-face
180//  and then map the appropriate texture layer to the part of the mesh corresponding to the original object
181//
182faceParVal=new FaceParameterValue[sel.length];
183for (h=0;h<sel.length;h++)
184{
185	// h is the texture number (i.e. h=0 is the texture of the first object, h=1 for second object etc.)
186	// i is the layer number
187	// texture numbers are ordered opposite to the layers they are in
188	//
189	texVal=new double[ftot];  // array to hold texture parameter values for each face
190	i=sel.length-1-h;   // layer number
191	texParams=new TextureParameter[texMap[h].getParameters().length];  // set up array for any texture parameters
192//	print(texMap[h].getParameters());
193	texParams=texMap[h].getParameters();  //get the texture parameters (if any) within the hth texture
194	//
195	tempParam=layMap.getLayerParameters(i);
196	faceParVal[i]=new FaceParameterValue(newMesh,tempParam[0]);
197	//  set the parameter values to 0 for the other parts of the mesh
198	for (f=0;f<fc;f++)
199	{
200		texVal[f]=0.0;
201	}
202	//  set the parameter values to 1 for the relevant faces
203	for (f=fc;f<fc+ObjInfo[h].object.getFaces().length;f++)
204	{
205		texVal[f]=1.0;
206	}
207	// set the parameter values to 0 for the other parts of the mesh
208	for (f=fc+ObjInfo[h].object.getFaces().length;f<ftot;f++)
209	{
210		texVal[f]=0.0;
211	}
212	newMesh.setParameterValue(tempParam[0],faceParVal[i]);
213	faceParVal[i].setValue(texVal);
214	//
215	// correct the texture mapping centre
216	//
217	// for linear 3D mapping
218	if (texMap[i] instanceof LinearMapping3D)
219	{
220		texScale=texMap[i].getScale();
221		texCent=texMap[i].getCenter();
222		scaleCorr=new Vec3(1/texScale.x,1/texScale.y,1/texScale.z);
223		scaleCorr.multiply(ObjInfo[i].coords.getOrigin());
224		texMap[i].setCenter(scaleCorr);
225	}
226	// for 2D projection mapping
227	if (texMap[i] instanceof ProjectionMapping)
228	{
229		texScale=texMap[i].getScale();
230		texCent=texMap[i].getCenter();
231		scaleCorr1=new Vec2(1/texScale.x,1/texScale.y);
232		scaleCorr=new Vec2(scaleCorr1.x*ObjInfo[i].coords.getOrigin().x+texCent.x,scaleCorr1.y*ObjInfo[i].coords.getOrigin().y+texCent.y);
233		texMap[i].setCenter(scaleCorr);
234	}
235	// for cylindrical or spherical mapping
236	if ((texMap[i] instanceof CylindricalMapping)||(texMap[i] instanceof SphericalMapping))
237	{
238	//	paramVal=ObjInfo[i].object.getParameterValues();  //get texture coordinates
239	//	texCoordsX=paramVal[0].getValue();
240	//	texCoordsY=paramVal[1].getValue();
241	//	texCoordsZ=paramVal[2].getValue();
242	//	for (q=0;q<texCoordsX.length;q++)
243	//	{
244	//		texCoordsX[q]=texCoordsX[q]+ObjInfo[i].coords.getOrigin().x;
245	//		texCoordsY[q]=texCoordsY[q]+ObjInfo[i].coords.getOrigin().y;
246	//		texCoordsZ[q]=texCoordsZ[q]+ObjInfo[i].coords.getOrigin().z;
247	//	}
248	//	paramVal[0].setValue(texCoordsX);
249	//	paramVal[1].setValue(texCoordsY);
250	//	paramVal[2].setValue(texCoordsZ);
251		texMap[i].setBoundToSurface(true);
252	}
253	layMap.setLayerMapping(h,texMap[i]);  // set the mapping correctly
254	// cycle through the texture parameters, determine the mapping type and set the values for the
255	// relevant part of the new mesh
256	//
257	for (j=0;j<texParams.length;j++)
258	{
259		paramVal=ObjInfo[h].object.getParameterValue(texParams[j]);  // determine mapping type
260		//
261		//  *** per-object texture parameters ***
262		if (paramVal instanceof ConstantParameterValue)
263		{
264			tpVal=new double[ftot]; // array to hold texture parameter values for each face of new mesh
265			val=paramVal.getValue(); // single value containing value of the parameter for the original object
266			//  set the parameter values to 0 for the other parts of the mesh
267			for (f=0;f<fc;f++)
268			{
269				tpVal[f]=0.0;
270			}
271			//  set the parameter values as per the original object for the relevant faces
272			for (f=fc;f<fc+ObjInfo[h].object.getFaces().length;f++)
273			{
274				tpVal[f]=val;
275			}
276			// set the parameter values to 0 for the other parts of the mesh
277			for (f=fc+ObjInfo[h].object.getFaces().length;f<ftot;f++)
278			{
279				tpVal[f]=0.0;
280			}
281			paramVal2=new FaceParameterValue(tpVal);  // make a new FaceParameterValue to apply to new mesh
282			newMesh.setParameterValue(texParams[j],paramVal2);  // apply it
283		}
284		//
285		//
286		// *** per-face texture parameters  ***
287		if (paramVal instanceof FaceParameterValue)
288		{
289			tpVal=new double[ftot];  // array to hold texture parameter values for each face of new mesh
290			valPerFace=paramVal.getValue();  // array containing values of the parameter for each face of original object
291			//  set the parameter values to 0 for the other parts of the mesh
292			for (f=0;f<fc;f++)
293			{
294				tpVal[f]=0.0;
295			}
296			//  set the parameter values as per the original object for the relevant faces
297			for (f=fc;f<fc+ObjInfo[h].object.getFaces().length;f++)
298			{
299				tpVal[f]=valPerFace[f-fc];
300			}
301			// set the parameter values to 0 for the other parts of the mesh
302			for (f=fc+ObjInfo[h].object.getFaces().length;f<ftot;f++)
303			{
304				tpVal[f]=0.0;
305			}
306			paramVal2=new FaceParameterValue(tpVal);  // make a new FaceParameterValue to apply to new mesh
307			newMesh.setParameterValue(texParams[j],paramVal2);  // apply it
308		}
309		//
310		//
311		// *** per-vertex parameters ***
312		if (paramVal instanceof VertexParameterValue)
313		{
314			tpVal=new double[vtot]; // array to hold texture parameter values for each vertex of new mesh
315			valPerVert=paramVal.getValue(); // array containing values of the parameter for each vertex of original object
316			//  set the parameter values to 0 for the other parts of the mesh
317			for (v=0;v<vc;v++)
318			{
319				tpVal[v]=0.0;
320			}
321			//  set the parameter values as per the original object for the relevant vertices
322			for (v=vc;v<vc+ObjInfo[h].object.getVertices().length;v++)
323			{
324				tpVal[v]=valPerVert[v-vc];
325			}
326			//  set the parameter values to 0 for the other parts of the mesh
327			for (v=vc+ObjInfo[h].object.getVertices().length;v<vtot;v++)
328			{
329				tpVal[v]=0.0;
330			}
331			paramVal2=new VertexParameterValue(tpVal); // make a new VertexParameterValue to apply to new mesh
332			newMesh.setParameterValue(texParams[j],paramVal2);  // apply it
333			print(paramVal2.getValue());
334		}
335		//
336		//
337		// *** per face-vertex texture parameters ***
338		if (paramVal instanceof FaceVertexParameterValue)
339		{
340			tpVal=new double[3][ftot]; // array to hold texture parameter values for each face-vertex of new mesh
341			valPerFace=paramVal.getValue(); //array containing values of the parameter for each face-vertex of original object
342			//  set the parameter values to 0 for the other parts of the mesh
343			for (f=0;f<fc;f++)
344			{
345				tpVal[0][f]=0.0;
346				tpVal[1][f]=0.0;
347				tpVal[2][f]=0.0;
348			}
349			//  set the parameter values as per the original object for the relevant face-vertices
350			for (f=fc;f<fc+ObjInfo[h].object.getFaces().length;f++)
351			{
352				tpVal[0][f]=valPerFace[0][f-fc];
353				tpVal[1][f]=valPerFace[1][f-fc];
354				tpVal[2][f]=valPerFace[2][f-fc];
355			}
356			// set the parameter values to 0 for the other parts of the mesh
357			for (f=fc+ObjInfo[h].object.getFaces().length;f<ftot;f++)
358			{
359				tpVal[0][f]=0.0;
360				tpVal[1][f]=0.0;
361				tpVal[2][f]=0.0;
362			}
363			paramVal2=new FaceVertexParameterValue(tpVal);  // make a new FaceVertexParameterValue to apply to new mesh
364			newMesh.setParameterValue(texParams[j],paramVal2);  // apply it
365		}
366	}
367
368	//
369	fc=fc+ObjInfo[h].object.getFaces().length;
370	vc=vc+ObjInfo[h].object.getVertices().length;
371}
372//
373//  add the new mesh to the scene.
374//
375newMesh.setTexture(layTex,layMap);   // apply the layered texture to the new mesh
376}
377//
378//
379//
380//  WELD
381//
382if (wantsWeld)
383{
384	vert=newVerts;
385	face=newMesh.getFaces();
386	edge=newEdges;
387	//
388	reassignV=new int[vert.length];
389	reposV=new int[vert.length];
390	numCoVert=0;
391	newVertIndex=new int [vert.length];
392	vToBeDel=new int[vert.length];
393	for (i=0;i<vert.length;i++)
394	{
395		reassignV[i]=-1;
396		reposV[i]=i;
397	}
398	//
399	for (v1=0;v1<vert.length;v1++)
400	{
401		for (v2=v1+1;v2<vert.length;v2++)
402		{
403			Dist=vert[v1].r.distance(vert[v2].r);
404			if ((Dist<weldTol)&&(reassignV[v2]==-1))
405			{
406				reassignV[v2]=v1;
407				for (k=v2+1;k<vert.length;k++)
408				{
409					reposV[k]--;
410				}
411				numCoVert++;
412			}
413		}
414	}
415	if (numCoVert==0)
416	{
417		new MessageDialog(window, "There are no vertices within the required tolerance");
418		return;
419	}
420	// build new index
421	for (i=0;i<vert.length;i++)
422	{
423		if (reassignV[i]!=-1) newVertIndex[i]=reposV[reassignV[i]]; else newVertIndex[i]=reposV[i];
424	}
425	//
426	// create new vertices skipping 'duplicates'
427	//
428	vCount=0;
429	newVert=new TriangleMesh.Vertex[vert.length-numCoVert];
430	for (i=0;i<vert.length;i++)   // cycle through the vertices
431	{
432		skip=0;
433		if (reassignV[i]==-1)
434		{
435			newVert[vCount]=vert[i];
436			vCount++;
437		}
438	}
439	//
440	// reassign the face vertices
441	faceInfo=new int [face.length][3];
442	for (f=0;f<face.length;f++)
443	{
444		faceInfo[f][0]=newVertIndex[face[f].v1];
445		faceInfo[f][1]=newVertIndex[face[f].v2];
446		faceInfo[f][2]=newVertIndex[face[f].v3];
447	}
448	//
449	newMesh.setShape(newVert,faceInfo);
450	}
451//
452//
453window.addObject(newMesh,new CoordinateSystem(),objname,null);
454