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