1 /****************************************************************************
2 * VCGLib                                                            o o     *
3 * Visual and Computer Graphics Library                            o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2004-2008                                           \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 #ifndef __VCGLIB_UTILDAE
24 #define __VCGLIB_UTILDAE
25 
26 
27 #include <wrap/io_trimesh/additionalinfo.h>
28 #include <vcg/complex/algorithms/update/normal.h>
29 #include <vcg/complex/algorithms/update/position.h>
30 
31 #include <wrap/io_trimesh/io_mask.h>
32 
33 #include <QDomDocument>
34 #include <QFile>
35 #include <QXmlStreamWriter>
36 #include <QStringList>
37 #include <QMap>
38 
39 #include <vcg/space/point3.h>
40 #include <vcg/space/texcoord2.h>
41 #include <vcg/space/color4.h>
42 #include <vcg/space/texcoord2.h>
43 #include <wrap/callback.h>
44 
45 #include <vector>
46 
47 namespace vcg {
48 namespace tri {
49 namespace io {
50 	class InfoDAE  : public AdditionalInfo
51 	{
52 		public:
53 
InfoDAE()54 		InfoDAE() :AdditionalInfo(){
55 			doc = NULL;
56 			textureIdMap.clear();
57 		}
58 
~InfoDAE()59 		~InfoDAE(){
60 			if(doc!=NULL) delete doc;
61 		}
62 
63 		QDomDocument* doc;
64 		QMap<QString,int> textureIdMap;
65 	};
66 
67 	class UtilDAE
68 	{
69 	public:
70 		enum DAEError
71 		{
72 			E_NOERROR,				// 0
73 			E_CANTOPEN,				// 1
74 			E_NOGEOMETRYLIBRARY,     // 2
75 			E_NOMESH,      // 3
76 			E_NOVERTEXPOSITION,            // 4
77 			E_NO3DVERTEXPOSITION,			// 5
78 			E_NO3DSCENE, // 6
79 			E_INCOMPATIBLECOLLADA141FORMAT, //7
80 			E_UNREFERENCEBLEDCOLLADAATTRIBUTE, // 8
81 			E_NOPOLYGONALMESH, //9
82 			E_CANTSAVE, //10
83 			E_NOACCESSORELEMENT
84 		};
85 
86 
ErrorMsg(int error)87 		static const char *ErrorMsg(int error)
88 		{
89 			static const char * dae_error_msg[] =
90 			{
91 				"No errors",
92 				"Can't open file",
93 				"File without a geometry library",
94 				"There isn't mesh in file",
95 				"The meshes in file haven't the vertex position attribute",
96 				"The importer assumes that the OpenMeshType uses a 3D point for the vertex position",
97 				"There isn't any scene in Collada file",
98 				"The input file is not compatible with COLLADA 1.41 standard format",
99 				"Collada file is trying to reference an attribute that is not in the file",
100 				"This version of Collada Importer support only triangular and polygonal mesh file"
101 			};
102 
103 			if(error>9 || error<0) return "Unknown error";
104 			else return dae_error_msg[error];
105 		};
106 	protected:
107 		// This function take care of removing the standard '#' from the beginning of the url
108 		// For example
109 	  // <instance_geometry url="#shape0-lib">
110 		// means that you have to search in the <library_geometries> for a node <geometry> with id = "shape0-lib"
111 		// you have to call this function to get the right reference name
112 
referenceToANodeAttribute(const QDomNode n,const QString & attr,QString & url_st)113 		inline static void referenceToANodeAttribute(const QDomNode n,const QString& attr,QString& url_st)
114 		{
115 			url_st = n.toElement().attribute(attr);
116 			int sz = url_st.size() - 1;
117 			url_st = url_st.right(sz);
118 			assert(url_st.size() != 0);
119 		}
120 
findNodeBySpecificAttributeValue(const QDomNodeList & ndl,const QString & attrname,const QString & attrvalue)121 		inline static QDomNode findNodeBySpecificAttributeValue(const QDomNodeList& ndl,const QString& attrname,const QString& attrvalue)
122 		{
123 			int ndl_size = ndl.size();
124 			int ind = 0;
125 			while(ind < ndl_size)
126 			{
127 				QString st = ndl.at(ind).toElement().attribute(attrname);
128 				if (st == attrvalue)
129 					return ndl.at(ind);
130 				++ind;
131 			}
132 			return QDomNode();
133 		}
134 
findNodeBySpecificAttributeValue(const QDomNode n,const QString & tag,const QString & attrname,const QString & attrvalue)135 		inline static QDomNode findNodeBySpecificAttributeValue(const QDomNode n,const QString& tag,const QString& attrname,const QString& attrvalue)
136 		{
137 			return findNodeBySpecificAttributeValue(n.toElement().elementsByTagName(tag),attrname,attrvalue);
138 		}
139 
findNodeBySpecificAttributeValue(const QDomDocument n,const QString & tag,const QString & attrname,const QString & attrvalue)140 		inline static QDomNode findNodeBySpecificAttributeValue(const QDomDocument n,const QString& tag,const QString& attrname,const QString& attrvalue)
141 		{
142 			return findNodeBySpecificAttributeValue(n.elementsByTagName(tag),attrname,attrvalue);
143 		}
144 
isThereTag(const QDomNodeList & list)145 		inline static bool isThereTag(const QDomNodeList& list)
146 		{
147 			return ((list.size() > 0) ? true : false);
148 		}
149 
isThereTag(const QDomNode n,const QString & tagname)150 		inline static bool isThereTag(const QDomNode n,const QString& tagname)
151 		{
152 			return isThereTag(n.toElement().elementsByTagName(tagname));
153 		}
154 
isThereTag(const QDomDocument n,const QString & tagname)155 		inline static bool isThereTag(const QDomDocument n,const QString& tagname)
156 		{
157 			return isThereTag(n.elementsByTagName(tagname));
158 		}
159 
160     // Very important function that given a <vertices> element find one of the its attribute (like position, color etc)
attributeSourcePerSimplex(const QDomNode n,const QDomDocument startpoint,const QString & sem)161 		inline static QDomNode attributeSourcePerSimplex(const QDomNode n,const QDomDocument startpoint,const QString& sem)
162 		{
163 			QDomNodeList vertattr = n.toElement().elementsByTagName("input");
164 			for(int ind = 0;ind < vertattr.size();++ind)
165 			{
166 				if (vertattr.at(ind).toElement().attribute("semantic") == sem)
167 				{
168 					QString url;
169 					referenceToANodeAttribute(vertattr.at(ind),"source",url);
170 					return findNodeBySpecificAttributeValue(startpoint,"source","id",url);
171 				}
172 			}
173 			return QDomNode();
174 		}
175 
176 		// This function is used to build up a list of strings that are scalar values.
valueStringList(QStringList & res,const QDomNode srcnode,const QString & tag)177 		inline static void valueStringList(QStringList& res,const QDomNode srcnode,const QString& tag)
178 		{
179 			QDomNodeList list = srcnode.toElement().elementsByTagName(tag);
180 			//assert(list.size() == 1);
181 			QString nd = list.at(0).firstChild().nodeValue();
182 			res = nd.simplified().split(" ",QString::SkipEmptyParts);
183             if(res.empty())
184                 {
185                     qDebug("Warning valueStringList returned and empty list. nothing inside element with tag '%s'", qPrintable(tag));
186                     return;
187                 }
188 			if (res.last() == "")
189 				res.removeLast();
190 
191 //			int emptyCount = res.removeAll(QString(""));
192 //			if(emptyCount>0) qDebug("- - - - - - - - valueStringList: Removed %i null strings when parsing tag %s",emptyCount,qPrintable(tag));
193 //			for(int i =0;i<res.size();++i)
194 //				qDebug("- - - - - - - - - - - - %3i = '%s'",i,qPrintable(res.at(i)));
195 
196 		}
197 
198 		/*inline static bool removeChildNode(QDomNodeList*/
199 
200 		inline static bool removeChildNodeList(QDomNodeList& nodelst,const QString& tag = "", const QString& attribname = "", const QString& attribvalue = "")
201 		{
202 			for(int jj = 0;jj < nodelst.size();++jj)
203 			{
204 				removeChildNode(nodelst.at(jj),tag,attribname,attribvalue);
205 			}
206 			return true;
207 		}
208 
209 
210 		inline static bool removeChildNode(QDomNode node,const QString& tag = "", const QString& attribname = "", const QString& attribvalue = "")
211 		{
212 			QDomNodeList clst = node.childNodes();
213 			for(int ii = 0;ii < clst.size();++ii)
214 			{
215 				QDomNode oldchild = node.childNodes().at(ii);
216 				if (tag != "")
217 				{
218 					if ((attribname != "") && (attribvalue != ""))
219 					{
220 						if (clst.at(ii).toElement().attribute(attribname) == attribvalue)
221 							node.removeChild(oldchild);
222 					}
223 					else
224 					{
225 						QString nm = clst.at(ii).nodeName();
226 						if (clst.at(ii).nodeName() == tag)
227 						{
228 							node.removeChild(oldchild);
229 						}
230 					}
231 				}
232 				else node.removeChild(oldchild);
233 			}
234 			return true;
235 		}
236 
ParseRotationMatrix(vcg::Matrix44f & m,const std::vector<QDomNode> & t)237 		static void ParseRotationMatrix(vcg::Matrix44f& m,const std::vector<QDomNode>& t)
238 		{
239 			vcg::Matrix44f rotTmp;
240 			vcg::Matrix44f tmp;
241 			rotTmp.SetIdentity();
242 			tmp.SetIdentity();
243 			for(unsigned int ii = 0;ii < t.size();++ii)
244 			{
245 				QString rt = t[ii].firstChild().nodeValue();
246 				QStringList rtl = rt.split(" ");
247 				if (rtl.last() == "") rtl.removeLast();
248 				assert(rtl.size() == 4);
249 				tmp.SetRotateDeg(rtl.at(3).toFloat(),vcg::Point3f(rtl.at(0).toFloat(),rtl.at(1).toFloat(),rtl.at(2).toFloat()));
250 				rotTmp = rotTmp*tmp;
251 			}
252 			m = m * rotTmp;
253 		}
254 
ParseTranslation(vcg::Matrix44f & m,const QDomNode t)255 		static void ParseTranslation(vcg::Matrix44f& m,const QDomNode t)
256 		{
257 			assert(t.toElement().tagName() == "translate");
258 			QDomNode tr = t.firstChild();
259 			QString coord = tr.nodeValue();
260 			QStringList coordlist = coord.split(" ");
261 			if (coordlist.last() == "")
262 				coordlist.removeLast();
263 			assert(coordlist.size() == 3);
264 			m[0][0] = 1.0f;
265 			m[1][1] = 1.0f;
266 			m[2][2] = 1.0f;
267 			m[3][3] = 1.0f;
268 			m[0][3] = coordlist.at(0).toFloat();
269 			m[1][3] = coordlist.at(1).toFloat();
270 			m[2][3] = coordlist.at(2).toFloat();
271 		}
272 
ParseMatrixNode(vcg::Matrix44f & m,const QDomNode t)273 		static void ParseMatrixNode(vcg::Matrix44f& m,const QDomNode t)
274 		{
275 			assert(t.toElement().tagName() == "matrix");
276 			QDomNode tr = t.firstChild();
277 			QString coord = tr.nodeValue().simplified();
278 			qDebug("Parsing matrix node; text value is '%s'",qPrintable(coord));
279 			QStringList coordlist = coord.split(" ");
280 			if (coordlist.last() == "")
281 				coordlist.removeLast();
282 			assert(coordlist.size() == 16);
283 			for(int i=0;i<4;++i)
284 			{
285 				m[i][0] = coordlist.at(i*4+0).toFloat();
286 				m[i][1] = coordlist.at(i*4+1).toFloat();
287 				m[i][2] = coordlist.at(i*4+2).toFloat();
288 				m[i][3] = coordlist.at(i*4+3).toFloat();
289 			}
290 		}
291 
TransfMatrix(const QDomNode parentnode,const QDomNode presentnode,vcg::Matrix44f & m)292 		static void TransfMatrix(const QDomNode parentnode,const QDomNode presentnode,vcg::Matrix44f& m)
293 		{
294 			if (presentnode == parentnode) return;
295 			else
296 			{
297 				QDomNode par = presentnode.parentNode();
298 				std::vector<QDomNode> rotlist;
299 				QDomNode trans;
300 				for(int ch = 0;ch < par.childNodes().size();++ch)
301 				{
302 					if (par.childNodes().at(ch).nodeName() == "rotate")
303 						rotlist.push_back(par.childNodes().at(ch));
304 					else if (par.childNodes().at(ch).nodeName() == "translate")
305 						 {
306 							trans = par.childNodes().at(ch);
307 					     }
308 				}
309 				vcg::Matrix44f tmp;
310 				tmp.SetIdentity();
311 				if (!trans.isNull()) ParseTranslation(tmp,trans);
312 				ParseRotationMatrix(tmp,rotlist);
313 				m = m * tmp;
314 				TransfMatrix(parentnode,par,m);
315 			}
316 		}
317 
findOffSetForASingleSimplex(QDomNode node)318 		inline static int findOffSetForASingleSimplex(QDomNode node)
319 		{
320 			QDomNodeList wedatts = node.toElement().elementsByTagName("input");
321 			int max = 0;
322 			if (wedatts.size() == 0) return -1;
323 			else
324 			{
325 				for(int ii = 0;ii < wedatts.size();++ii)
326 				{
327 					int tmp = wedatts.at(ii).toElement().attribute("offset").toInt();
328 					if (tmp > max) max = tmp;
329 				}
330 			}
331 			return max + 1;
332 		}
333 
findStringListAttribute(QStringList & list,const QDomNode node,const QDomNode poly,const QDomDocument startpoint,const char * token)334 		inline static int findStringListAttribute(QStringList& list,const QDomNode node,const QDomNode poly,const QDomDocument startpoint,const char* token)
335 		{
336 			int offset = 0;
337 			if (!node.isNull())
338 			{
339 				offset = node.toElement().attribute("offset").toInt();
340 				QDomNode st = attributeSourcePerSimplex(poly,startpoint,token);
341 				valueStringList(list,st,"float_array");
342 			}
343 			return offset;
344 		}
345 
346 
347 
348 
349 
350 		/* Very important procedure
351 			it has the task to find the name of the image node corresponding to a given material id,
352 			it assuemes that the material name that is passed have already been bound with the current bindings
353 		*/
354 
textureFinder(QString & boundMaterialName,QString & textureFileName,const QDomDocument doc)355 		inline static QDomNode textureFinder(QString& boundMaterialName, QString &textureFileName, const QDomDocument doc)
356 		{
357 			boundMaterialName.remove('#');
358 			//library_material -> material -> instance_effect
359 			QDomNodeList lib_mat = doc.elementsByTagName("library_materials");
360 			if (lib_mat.size() != 1)
361 				return QDomNode();
362 			QDomNode material = findNodeBySpecificAttributeValue(lib_mat.at(0),QString("material"),QString("id"),boundMaterialName);
363 			if (material.isNull())
364 				return QDomNode();
365 			QDomNodeList in_eff = material.toElement().elementsByTagName("instance_effect");
366 			if (in_eff.size() == 0)
367 				return QDomNode();
368 			QString url = in_eff.at(0).toElement().attribute("url");
369 			if ((url.isNull()) || (url == ""))
370 				return QDomNode();
371 			url = url.remove('#');
372       qDebug("====== searching among library_effects the effect with id '%s' ",qPrintable(url));
373 			//library_effects -> effect -> instance_effect
374 			QDomNodeList lib_eff = doc.elementsByTagName("library_effects");
375 			if (lib_eff.size() != 1)
376 				return QDomNode();
377 			QDomNode effect = findNodeBySpecificAttributeValue(lib_eff.at(0),QString("effect"),QString("id"),url);
378 			if (effect.isNull())
379 				return QDomNode();
380 			QDomNodeList init_from = effect.toElement().elementsByTagName("init_from");
381 			if (init_from.size() == 0)
382 				return QDomNode();
383 			QString img_id = init_from.at(0).toElement().text();
384 			if ((img_id.isNull()) || (img_id == ""))
385 				return QDomNode();
386 
387 			//library_images -> image
388 			QDomNodeList libraryImageNodeList = doc.elementsByTagName("library_images");
389 			qDebug("====== searching among library_images the effect with id '%s' ",qPrintable(img_id));
390 			if (libraryImageNodeList.size() != 1)
391 				return QDomNode();
392 			QDomNode imageNode = findNodeBySpecificAttributeValue(libraryImageNodeList.at(0),QString("image"),QString("id"),img_id);
393 			QDomNodeList initfromNode = imageNode.toElement().elementsByTagName("init_from");
394 			textureFileName= initfromNode.at(0).firstChild().nodeValue();
395 			qDebug("====== the image '%s' has a %i init_from nodes text '%s'",qPrintable(img_id),initfromNode.size(),qPrintable(textureFileName));
396 
397 			return imageNode;
398 		}
399 
indexTextureByImgNode(const QDomDocument doc,const QDomNode node)400 		static int indexTextureByImgNode(const QDomDocument doc,const QDomNode node)
401 		{
402 			QDomNodeList libim = doc.elementsByTagName(QString("library_images"));
403 			if (libim.size() != 1)
404 				return -1;
405 			QDomNodeList imgs = libim.at(0).toElement().elementsByTagName("image");
406 
407 			int ii = 0;
408 			bool found = false;
409 			while((ii < imgs.size()) && (!found))
410 			{
411 				if (imgs.at(ii) == node)
412 					found = true;
413 				else ++ii;
414 			}
415 			if (found)
416 				return ii;
417 			else
418 				return -1;
419 		}
420 
421 		struct WedgeAttribute
422 		{
423 			QDomNode wnsrc;
424 			QStringList wn;
425 			int offnm;
426 
427 			QDomNode wtsrc;
428 			QStringList wt;
429 			int stridetx;
430 			int offtx;
431 
432 			QDomNode wcsrc;
433 			QStringList wc;
434 			int stridecl;
435 			int offcl;
436 		};
437 	};
438 }
439 }
440 }
441 
442 #endif
443