colladaUtils.cpp 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include <algorithm>
  23. #include "console/console.h"
  24. #include "gfx/bitmap/gBitmap.h"
  25. #include "ts/collada/colladaUtils.h"
  26. #include "materials/matInstance.h"
  27. using namespace ColladaUtils;
  28. #define MAX_PATH_LENGTH 256
  29. // Helper macro to create Collada elements
  30. #define CREATE_ELEMENT(container, name, type) \
  31. type* name = daeSafeCast<type>(container->createAndPlace(#name));
  32. ColladaUtils::ImportOptions& ColladaUtils::getOptions()
  33. {
  34. static ImportOptions options;
  35. return options;
  36. }
  37. //------------------------------------------------------------------------------
  38. // Utility functions
  39. // Convert a transform from the Collada model coordinate system to the DTS coordinate
  40. // system
  41. void ColladaUtils::convertTransform(MatrixF& mat)
  42. {
  43. MatrixF rot(true);
  44. switch (ColladaUtils::getOptions().upAxis)
  45. {
  46. case UPAXISTYPE_X_UP:
  47. // rotate 90 around Y-axis, then 90 around Z-axis
  48. rot(0,0) = 0.0f; rot(1,0) = 1.0f;
  49. rot(1,1) = 0.0f; rot(2,1) = 1.0f;
  50. rot(0,2) = 1.0f; rot(2,2) = 0.0f;
  51. // pre-multiply the transform by the rotation matrix
  52. mat.mulL(rot);
  53. break;
  54. case UPAXISTYPE_Y_UP:
  55. // rotate 180 around Y-axis, then 90 around X-axis
  56. rot(0,0) = -1.0f;
  57. rot(1,1) = 0.0f; rot(2,1) = 1.0f;
  58. rot(1,2) = 1.0f; rot(2,2) = 0.0f;
  59. // pre-multiply the transform by the rotation matrix
  60. mat.mulL(rot);
  61. break;
  62. case UPAXISTYPE_Z_UP:
  63. default:
  64. // nothing to do
  65. break;
  66. }
  67. }
  68. /// Find the COMMON profile element in an effect
  69. const domProfile_COMMON* ColladaUtils::findEffectCommonProfile(const domEffect* effect)
  70. {
  71. if (effect) {
  72. // Find the COMMON profile
  73. const domFx_profile_abstract_Array& profiles = effect->getFx_profile_abstract_array();
  74. for (S32 iProfile = 0; iProfile < profiles.getCount(); iProfile++) {
  75. if (profiles[iProfile]->getElementType() == COLLADA_TYPE::PROFILE_COMMON)
  76. return daeSafeCast<domProfile_COMMON>(profiles[iProfile]);
  77. }
  78. }
  79. return NULL;
  80. }
  81. /// Find the <diffuse> element in the COMMON profile of an effect
  82. const domCommon_color_or_texture_type_complexType* ColladaUtils::findEffectDiffuse(const domEffect* effect)
  83. {
  84. const domProfile_COMMON* profile = findEffectCommonProfile(effect);
  85. if (profile) {
  86. if (profile->getTechnique()->getLambert())
  87. return profile->getTechnique()->getLambert()->getDiffuse();
  88. else if (profile->getTechnique()->getPhong())
  89. return profile->getTechnique()->getPhong()->getDiffuse();
  90. else if (profile->getTechnique()->getBlinn())
  91. return profile->getTechnique()->getBlinn()->getDiffuse();
  92. }
  93. return NULL;
  94. }
  95. /// Find the <specular> element in the COMMON profile of an effect
  96. const domCommon_color_or_texture_type_complexType* ColladaUtils::findEffectSpecular(const domEffect* effect)
  97. {
  98. const domProfile_COMMON* profile = findEffectCommonProfile(effect);
  99. if (profile) {
  100. if (profile->getTechnique()->getLambert())
  101. return NULL; // no <specular> element for Lambert shader
  102. else if (profile->getTechnique()->getPhong())
  103. return profile->getTechnique()->getPhong()->getSpecular();
  104. else if (profile->getTechnique()->getBlinn())
  105. return profile->getTechnique()->getBlinn()->getSpecular();
  106. }
  107. return NULL;
  108. }
  109. const domFx_sampler2D_common_complexType* ColladaUtils::getTextureSampler(const domEffect* effect,
  110. const domCommon_color_or_texture_type_complexType* texture)
  111. {
  112. // <texture texture="new_param_SID">.<newparam>.<sampler2D>
  113. if (texture) {
  114. const domCommon_color_or_texture_type_complexType::domTexture* domTex = texture->getTexture();
  115. if (domTex && domTex->getTexture()) {
  116. daeSIDResolver resolver(const_cast<domEffect*>(effect), domTex->getTexture());
  117. const domCommon_newparam_type* param = daeSafeCast<domCommon_newparam_type>(resolver.getElement());
  118. if (param)
  119. return param->getSampler2D();
  120. }
  121. }
  122. return NULL;
  123. }
  124. String ColladaUtils::getSamplerImagePath(const domEffect* effect,
  125. const domFx_sampler2D_common_complexType* sampler2D)
  126. {
  127. // <sampler2D>.<source>.<newparam>.<surface>.<init_from>.<image>.<init_from>
  128. const domProfile_COMMON* profile = findEffectCommonProfile(effect);
  129. if (profile && sampler2D && sampler2D->getSource()) {
  130. // Resolve the SID to get the <surface> param
  131. daeSIDResolver resolver(const_cast<domProfile_COMMON*>(profile), sampler2D->getSource()->getValue());
  132. domCommon_newparam_type* surfaceParam = daeSafeCast<domCommon_newparam_type>(resolver.getElement());
  133. // Get the surface <init_from> element
  134. if (surfaceParam && surfaceParam->getSurface()) {
  135. const domFx_surface_init_common* surfaceInit = surfaceParam->getSurface()->getFx_surface_init_common();
  136. if (surfaceInit && surfaceInit->getInit_from_array().getCount()) {
  137. // Resolve the ID to get the <image>, then read the texture path
  138. xsIDREF& idRef = surfaceInit->getInit_from_array()[0]->getValue();
  139. const domImage* image = daeSafeCast<domImage>(idRef.getElement());
  140. if (image && image->getInit_from())
  141. return resolveImagePath(image);
  142. }
  143. }
  144. }
  145. return "";
  146. }
  147. // Resolve image path into something we can use.
  148. String ColladaUtils::resolveImagePath(const domImage* image)
  149. {
  150. // 1. If the URI string contains an absolute path, use it if
  151. // it is inside the Torque folder, otherwise force textures
  152. // to be in the same folder as the shape.
  153. // 2. If the URI string contains a relative path, append it
  154. // to the shape path (since materials.cs cannot handle
  155. // relative paths).
  156. Torque::Path imagePath;
  157. String imageStr(image->getInit_from()->getValue().originalStr().c_str());
  158. // Trim leading "file://"
  159. if (imageStr.compare("file://", 7) == 0)
  160. imageStr.erase(0, 7);
  161. // Trim leading slash from absolute windows paths. eg. /D:/
  162. if ((imageStr.compare("/", 1) == 0) && (imageStr.find(':') == 2))
  163. imageStr.erase(0, 1);
  164. // Replace %20 with space
  165. imageStr.replace("%20", " ");
  166. if (Platform::isFullPath(imageStr))
  167. {
  168. // Absolute path => check for outside the Torque game folder
  169. imagePath = String( Platform::makeRelativePathName(imageStr, Platform::getMainDotCsDir()) );
  170. if ( !imagePath.getRoot().isEmpty() || // different drive (eg. C:/ vs D:/)
  171. (imagePath.getPath().find("/") == 0) || // different OS (eg. /home vs C:/home)
  172. (imagePath.getPath().find("../") == 0) ) // same drive, outside Torque game folder
  173. {
  174. // Force these to the shape folder
  175. imagePath.setRoot("");
  176. imagePath.setPath("");
  177. }
  178. }
  179. else
  180. {
  181. // Relative path => prepend with shape path
  182. Torque::Path tempPath(imageStr);
  183. imagePath = TSShapeLoader::getShapePath();
  184. imagePath.appendPath(tempPath);
  185. imagePath.setFileName(tempPath.getFileName());
  186. }
  187. // No need to specify the path if it is in the same folder as the model
  188. if (imagePath.getPath() == TSShapeLoader::getShapePath().getPath())
  189. imagePath.setPath("");
  190. // Don't care about the extension
  191. imagePath.setExtension("");
  192. return imagePath.getFullPath();
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Construct the appropriate child class
  196. BasePrimitive* BasePrimitive::get(const daeElement* element)
  197. {
  198. switch (element->getElementType()) {
  199. case COLLADA_TYPE::TRIANGLES: return new ColladaPrimitive<domTriangles>(element);
  200. case COLLADA_TYPE::TRISTRIPS: return new ColladaPrimitive<domTristrips>(element);
  201. case COLLADA_TYPE::TRIFANS: return new ColladaPrimitive<domTrifans>(element);
  202. case COLLADA_TYPE::POLYGONS: return new ColladaPrimitive<domPolygons>(element);
  203. case COLLADA_TYPE::POLYLIST: return new ColladaPrimitive<domPolylist>(element);
  204. default: return 0;
  205. }
  206. }
  207. //------------------------------------------------------------------------------
  208. // Collada animation curves
  209. /// Determine which elements are being targeted
  210. void AnimData::parseTargetString(const char* target, S32 fullCount, const char* elements[])
  211. {
  212. // Assume targeting all elements at offset 0
  213. targetValueCount = fullCount;
  214. targetValueOffset = 0;
  215. // Check for array syntax: (n) or (n)(m)
  216. if (const char* p = dStrchr(target, '(')) {
  217. S32 indN, indM;
  218. if (dSscanf(p, "(%d)(%d)", &indN, &indM) == 2) {
  219. targetValueOffset = (indN * 4) + indM; // @todo: 4x4 matrix only
  220. targetValueCount = 1;
  221. }
  222. else if (dSscanf(p, "(%d)", &indN) == 1) {
  223. targetValueOffset = indN;
  224. targetValueCount = 1;
  225. }
  226. }
  227. else if (const char* p = dStrrchr(target, '.')) {
  228. // Check for named elements
  229. for (S32 iElem = 0; elements[iElem][0] != 0; iElem++) {
  230. if (!dStrcmp(p, elements[iElem])) {
  231. targetValueOffset = iElem;
  232. targetValueCount = 1;
  233. break;
  234. }
  235. }
  236. }
  237. }
  238. /// Solve the cubic spline B(s) = param for s
  239. F32 AnimData::invertParamCubic(F32 param, F32 x0, F32 x1, F32 x2, F32 x3) const
  240. {
  241. const F64 INVERTPARAMCUBIC_TOL = 1.0e-09;
  242. const F64 INVERTPARAMCUBIC_SMALLERTOL = 1.0e-20;
  243. const F64 INVERTPARAMCUBIC_MAXIT = 100;
  244. // check input value for outside range
  245. if ((param - x0) < INVERTPARAMCUBIC_SMALLERTOL)
  246. return 0.0f;
  247. else if ((x3 - param) < INVERTPARAMCUBIC_SMALLERTOL)
  248. return 1.0f;
  249. U32 iterations = 0;
  250. // de Casteljau Subdivision.
  251. F32 u = 0.0f;
  252. F32 v = 1.0f;
  253. while (iterations < INVERTPARAMCUBIC_MAXIT) {
  254. F64 a = (x0 + x1)*0.5f;
  255. F64 b = (x1 + x2)*0.5f;
  256. F64 c = (x2 + x3)*0.5f;
  257. F64 d = (a + b)*0.5f;
  258. F64 e = (b + c)*0.5f;
  259. F64 f = (d + e)*0.5f;
  260. if (mFabs(f - param) < INVERTPARAMCUBIC_TOL)
  261. break;
  262. if (f < param) {
  263. x0 = f;
  264. x1 = e;
  265. x2 = c;
  266. u = (u + v)*0.5f;
  267. }
  268. else {
  269. x1 = a;
  270. x2 = d;
  271. x3 = f;
  272. v = (u + v)*0.5f;
  273. }
  274. iterations++;
  275. }
  276. return mClampF((u+v)*0.5f, 0.0f, 1.0f);
  277. }
  278. /// Get the interpolated value at time 't'
  279. void AnimData::interpValue(F32 t, U32 offset, double* value) const
  280. {
  281. // handle degenerate animation data
  282. if (input.size() == 0)
  283. {
  284. *value = 0.0f;
  285. return;
  286. }
  287. else if (input.size() == 1)
  288. {
  289. *value = output.getStringArrayData(0)[offset];
  290. return;
  291. }
  292. // clamp time to valid range
  293. F32 curveStart = input.getFloatValue(0);
  294. F32 curveEnd = input.getFloatValue(input.size()-1);
  295. t = mClampF(t, curveStart, curveEnd);
  296. // find the index of the input keyframe BEFORE 't'
  297. S32 index;
  298. for (index = 0; index < input.size()-2; index++) {
  299. if (input.getFloatValue(index + 1) > t)
  300. break;
  301. }
  302. // get the data for the two control points either side of 't'
  303. Point2F v0;
  304. v0.x = input.getFloatValue(index);
  305. v0.y = output.getStringArrayData(index)[offset];
  306. Point2F v3;
  307. v3.x = input.getFloatValue(index + 1);
  308. v3.y = output.getStringArrayData(index + 1)[offset];
  309. // If spline interpolation is specified but the tangents are not available,
  310. // default to LINEAR.
  311. const char* interp_method = interpolation.getStringValue(index);
  312. if (dStrEqual(interp_method, "BEZIER") ||
  313. dStrEqual(interp_method, "HERMITE") ||
  314. dStrEqual(interp_method, "CARDINAL")) {
  315. const double* inArray = inTangent.getStringArrayData(index + 1);
  316. const double* outArray = outTangent.getStringArrayData(index);
  317. if (!inArray || !outArray)
  318. interp_method = "LINEAR";
  319. }
  320. if (dStrEqual(interp_method, "STEP")) {
  321. // STEP interpolation
  322. *value = v0.y;
  323. }
  324. else if (dStrEqual(interp_method, "BEZIER") ||
  325. dStrEqual(interp_method, "HERMITE") ||
  326. dStrEqual(interp_method, "CARDINAL") ||
  327. dStrEqual(interp_method, "BSPLINE"))
  328. {
  329. // Cubic spline interpolation. The only difference between the 4 supported
  330. // forms is in the calculation of the other 2 control points:
  331. // BEZIER: control points are specified explicitly
  332. // HERMITE: tangents are specified, need to offset to get the control points
  333. // CARDINAL: (baked) tangents are specified, need to offset to get the control points
  334. // BSPLINE: control points are based on previous and next points
  335. // Get the 2 extra control points
  336. Point2F v1, v2;
  337. if (dStrEqual(interp_method, "BSPLINE")) {
  338. // v0 and v3 are the center points => need to
  339. // get the control points before and after them
  340. v1 = v0;
  341. v2 = v3;
  342. if (index > 0) {
  343. v0.x = input.getFloatValue(index-1);
  344. v0.y = output.getStringArrayData(index-1)[offset];
  345. }
  346. else {
  347. // mirror P1 through P0
  348. v0 = v1 + (v1 - v2);
  349. }
  350. if (index < (input.size()-2)) {
  351. v3.x = input.getFloatValue(index+2);
  352. v3.y = output.getStringArrayData(index+2)[offset];
  353. }
  354. else {
  355. // mirror P0 through P1
  356. v3 = v2 + (v2 - v1);
  357. }
  358. }
  359. else {
  360. const double* inArray = inTangent.getStringArrayData(index + 1);
  361. const double* outArray = outTangent.getStringArrayData(index);
  362. if (output.stride() == inTangent.stride()) {
  363. // This degenerate form (1D control points) does 2 things wrong:
  364. // 1) it does not specify the key (time) value
  365. // 2) the control point is specified as a tangent for both bezier and hermite
  366. // => interpolate to get the key values, and offset the tangent values
  367. v1.set((v0.x*2 + v3.x)/3, v0.y + outArray[offset]);
  368. v2.set((v0.x + v3.x*2)/3, v3.y - inArray[offset]);
  369. }
  370. else {
  371. // the expected form (2D control points)
  372. v1.set(outArray[offset*2], outArray[offset*2+1]);
  373. v2.set(inArray[offset*2], inArray[offset*2+1]);
  374. // if this is a hermite or cardinal spline, treat the values as tangents
  375. if (dStrEqual(interp_method, "HERMITE") || dStrEqual(interp_method, "CARDINAL")) {
  376. v1.set(v0.x + v1.x, v3.y - v1.y);
  377. v2.set(v0.x + v2.x, v3.x - v2.y);
  378. }
  379. }
  380. }
  381. // find 's' that gives the desired 't' value
  382. F32 s = invertParamCubic(t, v0.x, v1.x, v2.x, v3.x);
  383. // Calculate the output value using Bernstein evaluation and the
  384. // computed 's' value
  385. F32 c = 3.0f*(v1.y - v0.y);
  386. F32 e = 3.0f*(v2.y - v1.y);
  387. *value = (((v3.y - v0.y - e)*s + e - c)*s + c)*s + v0.y;
  388. }
  389. else {
  390. // default to LINEAR interpolation
  391. F32 s = mClampF((t - v0.x) / (v3.x - v0.x), 0.0f, 1.0f);
  392. *value = v0.y + (v3.y - v0.y) * s;
  393. }
  394. }
  395. void AnimData::interpValue(F32 t, U32 offset, const char** value) const
  396. {
  397. if (input.size() == 0)
  398. *value = "";
  399. else if (input.size() == 1)
  400. *value = output.getStringValue(0);
  401. else
  402. {
  403. // clamp time to valid range
  404. F32 curveStart = input.getFloatValue(0);
  405. F32 curveEnd = input.getFloatValue(input.size()-1);
  406. t = mClampF(t, curveStart, curveEnd);
  407. // find the index of the input keyframe BEFORE 't'
  408. S32 index;
  409. for (index = 0; index < input.size()-2; index++) {
  410. if (input.getFloatValue(index + 1) > t)
  411. break;
  412. }
  413. // String values only support STEP interpolation, so just get the
  414. // value at the input keyframe
  415. *value = output.getStringValue(index);
  416. }
  417. }
  418. //------------------------------------------------------------------------------
  419. // Collada document conditioners
  420. static void conditioner_fixupTextureSIDs(domCOLLADA* root)
  421. {
  422. for (S32 iLib = 0; iLib < root->getLibrary_effects_array().getCount(); iLib++) {
  423. domLibrary_effects* lib = root->getLibrary_effects_array()[iLib];
  424. for (S32 iEffect = 0; iEffect < lib->getEffect_array().getCount(); iEffect++) {
  425. domEffect* effect = lib->getEffect_array()[iEffect];
  426. const domCommon_color_or_texture_type_complexType* diffuse = findEffectDiffuse(effect);
  427. if (!diffuse || !diffuse->getTexture())
  428. continue;
  429. // Resolve the SID => if it is an <image>, add <sampler2D> and
  430. // <surface> elements to conform to the Collada spec.
  431. const char *image_sid = diffuse->getTexture()->getTexture();
  432. daeSIDResolver resolver(effect, image_sid);
  433. if (!daeSafeCast<domImage>(resolver.getElement()))
  434. continue;
  435. daeErrorHandler::get()->handleWarning(avar("Fixup %s <diffuse>.<texture> "
  436. "pointing at <image> instead of <sampler2D>", effect->getID()));
  437. // Generate SIDs for the new sampler2D and surface elements
  438. std::string sampler_sid(std::string(image_sid) + "-sampler");
  439. std::string surface_sid(std::string(image_sid) + "-surface");
  440. domProfile_COMMON* profile = const_cast<domProfile_COMMON*>(findEffectCommonProfile(effect));
  441. // Create <newparam>.<sampler2D>.<source>
  442. {
  443. CREATE_ELEMENT(profile, newparam, domCommon_newparam_type)
  444. CREATE_ELEMENT(newparam, sampler2D, domFx_sampler2D_common)
  445. CREATE_ELEMENT(sampler2D, source, domFx_sampler2D_common_complexType::domSource)
  446. newparam->setSid(sampler_sid.c_str());
  447. source->setValue(surface_sid.c_str());
  448. }
  449. // Create <newparam>.<surface>.<init_from>
  450. {
  451. CREATE_ELEMENT(profile, newparam, domCommon_newparam_type)
  452. CREATE_ELEMENT(newparam, surface, domFx_surface_common)
  453. CREATE_ELEMENT(surface, init_from, domFx_surface_init_from_common)
  454. CREATE_ELEMENT(surface, format, domFx_surface_common_complexType::domFormat)
  455. newparam->setSid(surface_sid.c_str());
  456. surface->setType(FX_SURFACE_TYPE_ENUM_2D);
  457. format->setValue("A8R8G8B8");
  458. init_from->setValue(image_sid);
  459. }
  460. // Store sampler2D sid in the <diffuse>.<texture> "texture" attribute
  461. diffuse->getTexture()->setTexture(sampler_sid.c_str());
  462. }
  463. }
  464. }
  465. static void conditioner_fixupImageURIs(domCOLLADA* root)
  466. {
  467. for (S32 iLib = 0; iLib < root->getLibrary_images_array().getCount(); iLib++) {
  468. domLibrary_images* lib = root->getLibrary_images_array()[iLib];
  469. for (S32 iImage = 0; iImage < lib->getImage_array().getCount(); iImage++) {
  470. domImage* image = lib->getImage_array()[iImage];
  471. if (image->getInit_from()) {
  472. xsAnyURI& uri = image->getInit_from()->getValue();
  473. // Replace '\' with '/'
  474. if (uri.originalStr().find("\\") != std::string::npos) {
  475. daeErrorHandler::get()->handleWarning(avar("Fixup invalid URI "
  476. "in %s: \"%s\"", image->getID(), uri.originalStr().c_str()));
  477. std::string str(uri.originalStr());
  478. std::replace(str.begin(), str.end(), '\\', '/');
  479. uri.set(str);
  480. }
  481. // Detect file://texture.jpg => this is an invalid URI and will
  482. // not be parsed correctly
  483. if (uri.scheme() == "file" &&
  484. uri.pathFile().empty() &&
  485. !uri.authority().empty()) {
  486. daeErrorHandler::get()->handleWarning(avar("Fixup invalid URI "
  487. "in %s: \"%s\"", image->getID(), uri.originalStr().c_str()));
  488. uri.set(uri.authority());
  489. }
  490. }
  491. }
  492. }
  493. }
  494. static void conditioner_fixupTransparency(domCOLLADA* root)
  495. {
  496. // Transparency is another example of something simple made complicated by
  497. // Collada. There are two (optional) elements that determine transparency:
  498. //
  499. // <transparent>: a color
  500. // <transparency>: a percentage applied to the color values
  501. //
  502. // Additionally, <transparent> has an optional "opaque" attribute that changes
  503. // the way transparency is determined. If set to A_ONE (the default), only the
  504. // alpha value of the transparent color is used, and a value of "1" means fully
  505. // opaque. If set to RGB_ZERO, only the RGB values of transparent are used, and
  506. // a value of "0" means fully opaque.
  507. //
  508. // To further complicate matters, Google Sketchup (all versions) and FeelingSoftware
  509. // ColladaMax (pre 3.03) export materials with the transparency element inverted
  510. // (1-transparency)
  511. // Get the <authoring_tool> string
  512. const char *authoringTool = "";
  513. if (const domAsset* asset = root->getAsset()) {
  514. for (S32 iContrib = 0; iContrib < asset->getContributor_array().getCount(); iContrib++) {
  515. const domAsset::domContributor* contrib = asset->getContributor_array()[iContrib];
  516. if (contrib->getAuthoring_tool()) {
  517. authoringTool = contrib->getAuthoring_tool()->getValue();
  518. break;
  519. }
  520. }
  521. }
  522. // Check for a match with the known problem-tools
  523. bool invertTransparency = false;
  524. const char *toolNames[] = { "FBX COLLADA exporter", "Google SketchUp",
  525. "Illusoft Collada Exporter", "FCollada" };
  526. for (S32 iName = 0; iName < (sizeof(toolNames)/sizeof(toolNames[0])); iName++) {
  527. if (dStrstr(authoringTool, toolNames[iName])) {
  528. invertTransparency = true;
  529. break;
  530. }
  531. }
  532. if (!invertTransparency)
  533. return;
  534. // Invert transparency as required for each effect
  535. for (S32 iLib = 0; iLib < root->getLibrary_effects_array().getCount(); iLib++) {
  536. domLibrary_effects* lib = root->getLibrary_effects_array()[iLib];
  537. for (S32 iEffect = 0; iEffect < lib->getEffect_array().getCount(); iEffect++) {
  538. domEffect* effect = lib->getEffect_array()[iEffect];
  539. // Find the common profile
  540. const domProfile_COMMON* commonProfile = findEffectCommonProfile(effect);
  541. if (!commonProfile)
  542. continue;
  543. domCommon_transparent_type* transparent = 0;
  544. if (commonProfile->getTechnique()->getConstant())
  545. transparent = commonProfile->getTechnique()->getConstant()->getTransparent();
  546. else if (commonProfile->getTechnique()->getLambert())
  547. transparent = commonProfile->getTechnique()->getLambert()->getTransparent();
  548. else if (commonProfile->getTechnique()->getPhong())
  549. transparent = commonProfile->getTechnique()->getPhong()->getTransparent();
  550. else if (commonProfile->getTechnique()->getBlinn())
  551. transparent = commonProfile->getTechnique()->getBlinn()->getTransparent();
  552. if (!transparent)
  553. continue;
  554. // If the shader "opaque" attribute is not specified, set it to
  555. // RGB_ZERO (the opposite of the Collada default), as this is what
  556. // the bad exporter tools seem to assume.
  557. if (!transparent->isAttributeSet("opaque")) {
  558. daeErrorHandler::get()->handleWarning(avar("Setting <transparent> "
  559. "\"opaque\" attribute to RGB_ZERO for %s <effect>", effect->getID()));
  560. transparent->setOpaque(FX_OPAQUE_ENUM_RGB_ZERO);
  561. }
  562. }
  563. }
  564. }
  565. static void conditioner_checkBindShapeMatrix(domCOLLADA* root)
  566. {
  567. for (S32 iLib = 0; iLib < root->getLibrary_controllers_array().getCount(); iLib++) {
  568. domLibrary_controllers* lib = root->getLibrary_controllers_array().get(iLib);
  569. for (S32 iCon = 0; iCon < lib->getController_array().getCount(); iCon++) {
  570. domController* con = lib->getController_array().get(iCon);
  571. if (con->getSkin() && con->getSkin()->getBind_shape_matrix()) {
  572. MatrixF mat = vecToMatrixF<domMatrix>(con->getSkin()->getBind_shape_matrix()->getValue());
  573. if (!mat.fullInverse()) {
  574. daeErrorHandler::get()->handleWarning(avar("<bind_shape_matrix> "
  575. "in %s <controller> is not invertible (may cause problems with "
  576. "skinning)", con->getID()));
  577. }
  578. }
  579. }
  580. }
  581. }
  582. static void conditioner_fixupVertexWeightJoints(domCOLLADA* root)
  583. {
  584. for (S32 iLib = 0; iLib < root->getLibrary_controllers_array().getCount(); iLib++) {
  585. domLibrary_controllers* lib = root->getLibrary_controllers_array().get(iLib);
  586. for (S32 iCon = 0; iCon < lib->getController_array().getCount(); iCon++) {
  587. domController* con = lib->getController_array().get(iCon);
  588. if (con->getSkin() && con->getSkin()->getVertex_weights())
  589. {
  590. domInputLocalOffset_Array& vw_inputs = con->getSkin()->getVertex_weights()->getInput_array();
  591. for (S32 vInput = 0; vInput < vw_inputs.getCount(); vInput++) {
  592. domInputLocalOffset *vw_input = vw_inputs.get(vInput);
  593. if (dStrEqual(vw_input->getSemantic(), "JOINT")) {
  594. // Check if this input points at a float array (bad)
  595. domSource* vw_source = daeSafeCast<domSource>(vw_input->getSource().getElement());
  596. if (vw_source->getFloat_array()) {
  597. // Copy the value from the <joints> JOINTS input instead
  598. domInputLocal_Array& joint_inputs = con->getSkin()->getJoints()->getInput_array();
  599. for (S32 jInput = 0; jInput < joint_inputs.getCount(); jInput++) {
  600. domInputLocal *joint_input = joint_inputs.get(jInput);
  601. if (dStrEqual(joint_input->getSemantic(), "JOINT")) {
  602. vw_input->setSource(joint_input->getSource());
  603. break;
  604. }
  605. }
  606. }
  607. }
  608. }
  609. }
  610. }
  611. }
  612. }
  613. static void conditioner_createDefaultClip(domCOLLADA* root)
  614. {
  615. // Check if the document has any <animation_clip>s
  616. for (S32 iLib = 0; iLib < root->getLibrary_animation_clips_array().getCount(); iLib++) {
  617. if (root->getLibrary_animation_clips_array()[iLib]->getAnimation_clip_array().getCount())
  618. return;
  619. }
  620. // Get all top-level <animation>s into an array
  621. domAnimation_Array animations;
  622. for (S32 iAnimLib = 0; iAnimLib < root->getLibrary_animations_array().getCount(); iAnimLib++) {
  623. const domLibrary_animations* libraryAnims = root->getLibrary_animations_array()[iAnimLib];
  624. for (S32 iAnim = 0; iAnim < libraryAnims->getAnimation_array().getCount(); iAnim++)
  625. animations.append(libraryAnims->getAnimation_array()[iAnim]);
  626. }
  627. if (!animations.getCount())
  628. return;
  629. daeErrorHandler::get()->handleWarning("Creating cyclic animation clip to "
  630. "hold all animations");
  631. // Get animation_clip library (create one if necessary)
  632. if (!root->getLibrary_animation_clips_array().getCount()) {
  633. root->createAndPlace("library_animation_clips");
  634. }
  635. domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[0];
  636. // Create new animation_clip for the default sequence
  637. CREATE_ELEMENT(libraryClips, animation_clip, domAnimation_clip)
  638. animation_clip->setName("ambient");
  639. animation_clip->setId("dummy_ambient_clip");
  640. animation_clip->setStart(0);
  641. animation_clip->setEnd(0);
  642. // Add all top_level animations to the clip (sub-animations will be included
  643. // when the clip is procesed)
  644. for (S32 iAnim = 0; iAnim < animations.getCount(); iAnim++) {
  645. if (!animations[iAnim]->getId())
  646. animations[iAnim]->setId(avar("dummy-animation-id%d", iAnim));
  647. CREATE_ELEMENT(animation_clip, instance_animation, domInstanceWithExtra)
  648. std::string url(std::string("#") + animations[iAnim]->getId());
  649. instance_animation->setUrl(url.c_str());
  650. }
  651. // Add the 'Torque' profile to specify the 'Cyclic' flag
  652. CREATE_ELEMENT(animation_clip, extra, domExtra)
  653. CREATE_ELEMENT(extra, technique, domTechnique)
  654. CREATE_ELEMENT(technique, any, domAny)
  655. technique->setProfile("Torque");
  656. any->setElementName("cyclic");
  657. any->setValue("1");
  658. }
  659. static void conditioner_fixupAnimation(domAnimation* anim)
  660. {
  661. for (S32 iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) {
  662. // Get the animation elements: <channel>, <sampler>
  663. domChannel* channel = anim->getChannel_array()[iChannel];
  664. domSampler* sampler = daeSafeCast<domSampler>(channel->getSource().getElement());
  665. if (!sampler)
  666. continue;
  667. /*
  668. // If using a spline interpolation type but no tangents are specified,
  669. // fall back to LINEAR interpolation.
  670. bool isSpline = false;
  671. bool foundInTangent = false;
  672. bool foundOutTangent = false;
  673. for (int iInput = 0; iInput < sampler->getInput_array().getCount(); iInput++) {
  674. const char *semantic = sampler->getInput_array()[iInput]->getSemantic();
  675. if (dStrEqual(semantic, "INTERPOLATION")) {
  676. if (
  677. }
  678. if (dStrEqual(semantic, "IN_TANGENT"))
  679. foundInTangent = true;
  680. if (dStrEqual(semantic, "OUT_TANGENT"))
  681. foundOutTangent = true;
  682. }
  683. if (isSpline && (!foundInTangent || !foundOutTangent)) {
  684. daeErrorHandler::get()->handleWarning(avar("%s type interpolation "
  685. "specified for %s, but IN/OUT TANGENTS are not provided. Using "
  686. "LINEAR interpolation instead.");
  687. }
  688. */
  689. // Find the animation channel target
  690. daeSIDResolver resolver(channel, channel->getTarget());
  691. daeElement* target = resolver.getElement();
  692. if (!target) {
  693. // Some exporters generate visibility animations but don't add the
  694. // FCOLLADA extension, so the target doesn't actually exist! Detect
  695. // this situation and add the extension manually so the animation
  696. // still works.
  697. if (dStrEndsWith(channel->getTarget(), "/visibility")) {
  698. // Get parent SID string
  699. char *parentSID = dStrdup(channel->getTarget());
  700. parentSID[dStrlen(parentSID) - dStrlen("/visibility")] = '\0';
  701. // Find the parent element (should be a <node>)
  702. daeSIDResolver parentResolver(channel, parentSID);
  703. daeElement* parent = parentResolver.getElement();
  704. delete [] parentSID;
  705. if (parent && (parent->getElementType() == COLLADA_TYPE::NODE)) {
  706. // Create the FCOLLADA extension
  707. daeErrorHandler::get()->handleWarning(avar("Creating missing "
  708. "visibility animation target: %s", channel->getTarget()));
  709. // Check if the <visibility> element exists but is missing the SID
  710. daeElement* vis = parent->getDescendant("visibility");
  711. if (vis)
  712. {
  713. vis->setAttribute("sid", "visibility");
  714. }
  715. else
  716. {
  717. CREATE_ELEMENT(parent, extra, domExtra)
  718. CREATE_ELEMENT(extra, technique, domTechnique)
  719. CREATE_ELEMENT(technique, any, domAny)
  720. technique->setProfile("FCOLLADA");
  721. any->setElementName("visibility");
  722. any->setAttribute("sid", "visibility");
  723. any->setValue(""); // real initial value will be set when animation is processed in ColladaShapeLoader::processAnimation
  724. }
  725. }
  726. }
  727. }
  728. }
  729. // Process child animations
  730. for (S32 iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++)
  731. conditioner_fixupAnimation(anim->getAnimation_array()[iAnim]);
  732. }
  733. /// Apply the set of model conditioners
  734. void ColladaUtils::applyConditioners(domCOLLADA* root)
  735. {
  736. //--------------------------------------------------------------------------
  737. // The built-in MAX FBX exporter specifies an <image> SID in the <texture>
  738. // "texture" attribute instead of a <newparam> SID. Detect and fix this.
  739. conditioner_fixupTextureSIDs(root);
  740. //--------------------------------------------------------------------------
  741. // The built-in MAX FBX exporter also generates invalid URI paths in the
  742. // <image>.<init_from> tag, so fix that up too.
  743. conditioner_fixupImageURIs(root);
  744. //--------------------------------------------------------------------------
  745. // Many exporters get transparency backwards. Check if the model was exported
  746. // by one with a known issue and correct it.
  747. conditioner_fixupTransparency(root);
  748. //--------------------------------------------------------------------------
  749. // Some exporters (AutoDesk) generate invalid bind_shape matrices. Warn if
  750. // the bind_shape_matrix is not invertible.
  751. conditioner_checkBindShapeMatrix(root);
  752. //--------------------------------------------------------------------------
  753. // The PoserPro exporter points the <vertex_weights> JOINT input to the
  754. // inverse bind matrices instead of the joint names array. Detect and fix it.
  755. conditioner_fixupVertexWeightJoints(root);
  756. //--------------------------------------------------------------------------
  757. // If the model contains <animation>s but no <animation_clip>s, just put all
  758. // top level animations into a single clip.
  759. conditioner_createDefaultClip(root);
  760. //--------------------------------------------------------------------------
  761. // Apply some animation fixups:
  762. // 1) Some exporters (eg. Blender) generate "BEZIER" type animation curves,
  763. // but do not specify the IN and OUT tangent data arrays. Detect this and
  764. // fall back to LINEAR interpolation.
  765. // 2) Some exporters generate visibility animations but don't add the FCOLLADA
  766. // extension, so the target doesn't actually exist! Detect this situation
  767. // and add the extension manually so the animation still works.
  768. for (S32 iLib = 0; iLib < root->getLibrary_animations_array().getCount(); iLib++) {
  769. const domLibrary_animations* lib = root->getLibrary_animations_array()[iLib];
  770. for (S32 iAnim = 0; iAnim < lib->getAnimation_array().getCount(); iAnim++)
  771. conditioner_fixupAnimation(lib->getAnimation_array()[iAnim]);
  772. }
  773. }
  774. Torque::Path ColladaUtils::findTexture(const Torque::Path& diffuseMap)
  775. {
  776. Vector<Torque::Path> foundPaths;
  777. GBitmap::sFindFiles(diffuseMap, &foundPaths);
  778. if (foundPaths.size() > 0)
  779. return Torque::Path(foundPaths[0]);
  780. // If unable to load texture in current directory
  781. // look in the parent directory. But never look in the root.
  782. Torque::Path newPath(diffuseMap);
  783. String filePath = newPath.getPath();
  784. String::SizeType slash = filePath.find('/', filePath.length(), String::Right);
  785. if (slash != String::NPos)
  786. {
  787. slash = filePath.find('/', filePath.length(), String::Right);
  788. if (slash != String::NPos)
  789. {
  790. String truncPath = filePath.substr(0, slash);
  791. newPath.setPath(truncPath);
  792. return findTexture(newPath);
  793. }
  794. }
  795. return String::EmptyString;
  796. }
  797. void ColladaUtils::exportColladaHeader(TiXmlElement* rootNode)
  798. {
  799. TiXmlElement* assetNode = new TiXmlElement("asset");
  800. rootNode->LinkEndChild(assetNode);
  801. TiXmlElement* contributorNode = new TiXmlElement("contributor");
  802. assetNode->LinkEndChild(contributorNode);
  803. TiXmlElement* authorNode = new TiXmlElement("author");
  804. contributorNode->LinkEndChild(authorNode);
  805. TiXmlText* authorNodeText = new TiXmlText("Torque3D MIT User");
  806. authorNode->LinkEndChild(authorNodeText);
  807. TiXmlElement* authoringToolNode = new TiXmlElement("authoring_tool");
  808. contributorNode->LinkEndChild(authoringToolNode);
  809. TiXmlText* authorText = new TiXmlText(avar("%s %s Object Exporter", getEngineProductString(), getVersionString()));
  810. authoringToolNode->LinkEndChild(authorText);
  811. //TiXmlElement* commentsNode = new TiXmlElement("comments");
  812. //contributorNode->LinkEndChild(commentsNode);
  813. // Get the current time
  814. Platform::LocalTime lt;
  815. Platform::getLocalTime(lt);
  816. String localTime = Platform::localTimeToString(lt);
  817. localTime.replace('\t', ' ');
  818. TiXmlElement* createdNode = new TiXmlElement("created");
  819. assetNode->LinkEndChild(createdNode);
  820. TiXmlText* createdText = new TiXmlText(avar("%s", localTime.c_str()));
  821. createdNode->LinkEndChild(createdText);
  822. TiXmlElement* modifiedNode = new TiXmlElement("modified");
  823. assetNode->LinkEndChild(modifiedNode);
  824. TiXmlText* modifiedText = new TiXmlText(avar("%s", localTime.c_str()));
  825. modifiedNode->LinkEndChild(modifiedText);
  826. //TiXmlElement* revisionNode = new TiXmlElement("revision");
  827. //assetNode->LinkEndChild(revisionNode);
  828. //TiXmlElement* titleNode = new TiXmlElement("title");
  829. //assetNode->LinkEndChild(titleNode);
  830. //TiXmlElement* subjectNode = new TiXmlElement("subject");
  831. //assetNode->LinkEndChild(subjectNode);
  832. //TiXmlElement* keywordsNode = new TiXmlElement("keywords");
  833. //assetNode->LinkEndChild(keywordsNode);
  834. // Torque uses Z_UP with 1 unit equal to 1 meter by default
  835. TiXmlElement* unitNode = new TiXmlElement("unit");
  836. assetNode->LinkEndChild(unitNode);
  837. unitNode->SetAttribute("name", "meter");
  838. unitNode->SetAttribute("meter", "1");
  839. TiXmlElement* axisNode = new TiXmlElement("up_axis");
  840. assetNode->LinkEndChild(axisNode);
  841. TiXmlText* axisText = new TiXmlText("Z_UP");
  842. axisNode->LinkEndChild(axisText);
  843. }
  844. void ColladaUtils::exportColladaMaterials(TiXmlElement* rootNode, const OptimizedPolyList& mesh, Vector<String>& matNames, const Torque::Path& colladaFile)
  845. {
  846. // First the image library
  847. TiXmlElement* imgLibNode = new TiXmlElement("library_images");
  848. rootNode->LinkEndChild(imgLibNode);
  849. for (U32 i = 0; i < mesh.mMaterialList.size(); i++)
  850. {
  851. BaseMatInstance* baseInst = mesh.mMaterialList[i];
  852. matNames.push_back(String::ToString("Material%d", i));
  853. Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
  854. if (!mat)
  855. continue;
  856. String diffuseMap;
  857. if (mat->getName() && mat->getName()[0])
  858. matNames.last() = String(mat->getName());
  859. // Handle an auto-generated "Default Material" specially
  860. if (mat->isAutoGenerated())
  861. {
  862. Torque::Path diffusePath;
  863. if (mat->mDiffuseMapFilename[0].isNotEmpty())
  864. diffusePath = mat->mDiffuseMapFilename[0];
  865. else
  866. diffusePath = String("warningMat");
  867. matNames.last() = diffusePath.getFileName();
  868. diffuseMap += diffusePath.getFullFileName();
  869. }
  870. else
  871. {
  872. if (mat->mDiffuseMapFilename[0].isNotEmpty())
  873. diffuseMap += mat->mDiffuseMapFilename[0];
  874. else
  875. diffuseMap += "warningMat";
  876. }
  877. Torque::Path diffusePath = findTexture(colladaFile.getPath() + "/" + diffuseMap);
  878. // If we didn't get a path
  879. if (diffusePath.getFullPath().isNotEmpty())
  880. diffuseMap = Torque::Path::MakeRelativePath(diffusePath, colladaFile);
  881. TiXmlElement* imageNode = new TiXmlElement("image");
  882. imgLibNode->LinkEndChild(imageNode);
  883. imageNode->SetAttribute("id", avar("%s", matNames.last().c_str()));
  884. imageNode->SetAttribute("name", avar("%s", matNames.last().c_str()));
  885. TiXmlElement* initNode = new TiXmlElement("init_from");
  886. imageNode->LinkEndChild(initNode);
  887. TiXmlText* initText = new TiXmlText(avar("file://%s", diffuseMap.c_str())); // "the file://" is needed to load the texture in some old apps (ex: 3ds Max 2009)
  888. initNode->LinkEndChild(initText);
  889. }
  890. // Next the effects library
  891. TiXmlElement* effectLibNode = new TiXmlElement("library_effects");
  892. rootNode->LinkEndChild(effectLibNode);
  893. for (U32 i = 0; i < mesh.mMaterialList.size(); i++)
  894. {
  895. BaseMatInstance* baseInst = mesh.mMaterialList[i];
  896. Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
  897. if (!mat)
  898. continue;
  899. TiXmlElement* effectNode = new TiXmlElement("effect");
  900. effectLibNode->LinkEndChild(effectNode);
  901. effectNode->SetAttribute("id", avar("%s-effect", matNames[i].c_str()));
  902. effectNode->SetAttribute("name", avar("%s-effect", matNames[i].c_str()));
  903. TiXmlElement* profileNode = new TiXmlElement("profile_COMMON");
  904. effectNode->LinkEndChild(profileNode);
  905. // ---------------------------
  906. TiXmlElement* newParamNode = new TiXmlElement("newparam");
  907. profileNode->LinkEndChild(newParamNode);
  908. newParamNode->SetAttribute("sid", avar("%s-surface", matNames[i].c_str()));
  909. TiXmlElement* surfaceNode = new TiXmlElement("surface");
  910. newParamNode->LinkEndChild(surfaceNode);
  911. surfaceNode->SetAttribute("type", "2D");
  912. TiXmlElement* initNode2 = new TiXmlElement("init_from");
  913. surfaceNode->LinkEndChild(initNode2);
  914. TiXmlText* init2Text = new TiXmlText(avar("%s", matNames[i].c_str()));
  915. initNode2->LinkEndChild(init2Text);
  916. TiXmlElement* formatNode = new TiXmlElement("format");
  917. surfaceNode->LinkEndChild(formatNode);
  918. TiXmlText* formatText = new TiXmlText("A8R8G8B8");
  919. formatNode->LinkEndChild(formatText);
  920. // ---------------------------
  921. TiXmlElement* newParam2Node = new TiXmlElement("newparam");
  922. profileNode->LinkEndChild(newParam2Node);
  923. newParam2Node->SetAttribute("sid", avar("%s-sampler", matNames[i].c_str()));
  924. TiXmlElement* sampler2DNode = new TiXmlElement("sampler2D");
  925. newParam2Node->LinkEndChild(sampler2DNode);
  926. TiXmlElement* sourceSampler2DNode = new TiXmlElement("source");
  927. sampler2DNode->LinkEndChild(sourceSampler2DNode);
  928. TiXmlText* sourceSampler2DText = new TiXmlText(avar("%s-surface", matNames[i].c_str()));
  929. sourceSampler2DNode->LinkEndChild(sourceSampler2DText);
  930. // ---------------------------
  931. TiXmlElement* techniqueNode = new TiXmlElement("technique");
  932. profileNode->LinkEndChild(techniqueNode);
  933. techniqueNode->SetAttribute("sid", "common");
  934. TiXmlElement* blinnNode = new TiXmlElement("blinn");
  935. techniqueNode->LinkEndChild(blinnNode);
  936. // ---------------------------
  937. TiXmlElement* emissionNode = new TiXmlElement("emission");
  938. blinnNode->LinkEndChild(emissionNode);
  939. TiXmlElement* colorEmissionNode = new TiXmlElement("color");
  940. emissionNode->LinkEndChild(colorEmissionNode);
  941. colorEmissionNode->SetAttribute("sid", "emission");
  942. TiXmlText* colorEmissionNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
  943. colorEmissionNode->LinkEndChild(colorEmissionNodeText);
  944. // ---------------------------
  945. TiXmlElement* ambientNode = new TiXmlElement("ambient");
  946. blinnNode->LinkEndChild(ambientNode);
  947. TiXmlElement* colorAmbientNode = new TiXmlElement("color");
  948. ambientNode->LinkEndChild(colorAmbientNode);
  949. colorAmbientNode->SetAttribute("sid", "ambient");
  950. TiXmlText* colorAmbientNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
  951. colorAmbientNode->LinkEndChild(colorAmbientNodeText);
  952. // ---------------------------
  953. TiXmlElement* diffuseNode = new TiXmlElement("diffuse");
  954. blinnNode->LinkEndChild(diffuseNode);
  955. TiXmlElement* textureDiffuseNode = new TiXmlElement("texture");
  956. diffuseNode->LinkEndChild(textureDiffuseNode);
  957. textureDiffuseNode->SetAttribute("texture", avar("%s-sampler", matNames[i].c_str()));
  958. textureDiffuseNode->SetAttribute("texcoord", "UVMap");
  959. // Extra info useful for getting the texture to show up correctly in MAYA and 3DS Max
  960. TiXmlElement* extraNode = new TiXmlElement("extra");
  961. textureDiffuseNode->LinkEndChild(extraNode);
  962. TiXmlElement* extraTechNode = new TiXmlElement("technique");
  963. extraNode->LinkEndChild(extraTechNode);
  964. extraTechNode->SetAttribute("profile", "MAYA");
  965. TiXmlElement* extraWrapUNode = new TiXmlElement("wrapU");
  966. extraTechNode->LinkEndChild(extraWrapUNode);
  967. extraWrapUNode->SetAttribute("sid", "wrapU0");
  968. TiXmlText* extraWrapUText = new TiXmlText("TRUE");
  969. extraWrapUNode->LinkEndChild(extraWrapUText);
  970. TiXmlElement* extraWrapVNode = new TiXmlElement("wrapV");
  971. extraTechNode->LinkEndChild(extraWrapVNode);
  972. extraWrapVNode->SetAttribute("sid", "wrapV0");
  973. TiXmlText* extraWrapVText = new TiXmlText("TRUE");
  974. extraWrapVNode->LinkEndChild(extraWrapVText);
  975. TiXmlElement* extraBlendNode = new TiXmlElement("blend_mode");
  976. extraTechNode->LinkEndChild(extraBlendNode);
  977. TiXmlText* extraBlendText = new TiXmlText("ADD");
  978. extraBlendNode->LinkEndChild(extraBlendText);
  979. // ---------------------------
  980. TiXmlElement* specularNode = new TiXmlElement("specular");
  981. blinnNode->LinkEndChild(specularNode);
  982. TiXmlElement* colorSpecularNode = new TiXmlElement("color");
  983. specularNode->LinkEndChild(colorSpecularNode);
  984. colorSpecularNode->SetAttribute("sid", "specular");
  985. TiXmlText* colorSpecularNodeText = new TiXmlText("0.5 0.5 0.5 1.0");
  986. colorSpecularNode->LinkEndChild(colorSpecularNodeText);
  987. // ---------------------------
  988. TiXmlElement* shininessNode = new TiXmlElement("shininess");
  989. blinnNode->LinkEndChild(shininessNode);
  990. TiXmlElement* colorShininessNode = new TiXmlElement("float");
  991. shininessNode->LinkEndChild(colorShininessNode);
  992. colorShininessNode->SetAttribute("sid", "shininess");
  993. TiXmlText* colorShininessNodeText = new TiXmlText("1.0");
  994. colorShininessNode->LinkEndChild(colorShininessNodeText);
  995. // ---------------------------
  996. TiXmlElement* reflectiveNode = new TiXmlElement("reflective");
  997. blinnNode->LinkEndChild(reflectiveNode);
  998. TiXmlElement* colorReflectiveNode = new TiXmlElement("color");
  999. reflectiveNode->LinkEndChild(colorReflectiveNode);
  1000. colorReflectiveNode->SetAttribute("sid", "reflective");
  1001. TiXmlText* colorReflectiveNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
  1002. colorReflectiveNode->LinkEndChild(colorReflectiveNodeText);
  1003. // ---------------------------
  1004. TiXmlElement* reflectivityNode = new TiXmlElement("reflectivity");
  1005. blinnNode->LinkEndChild(reflectivityNode);
  1006. TiXmlElement* floatReflectivityNode = new TiXmlElement("float");
  1007. reflectivityNode->LinkEndChild(floatReflectivityNode);
  1008. floatReflectivityNode->SetAttribute("sid", "reflectivity");
  1009. TiXmlText* floatReflectivityText = new TiXmlText("0.5");
  1010. floatReflectivityNode->LinkEndChild(floatReflectivityText);
  1011. // ---------------------------
  1012. TiXmlElement* transparentNode = new TiXmlElement("transparent");
  1013. blinnNode->LinkEndChild(transparentNode);
  1014. transparentNode->SetAttribute("opaque", "RGB_ZERO");
  1015. TiXmlElement* colorTransparentNode = new TiXmlElement("color");
  1016. transparentNode->LinkEndChild(colorTransparentNode);
  1017. colorTransparentNode->SetAttribute("sid", "transparent");
  1018. TiXmlText* colorTransparentNodeText = new TiXmlText("0.0 0.0 0.0 1.0");
  1019. colorTransparentNode->LinkEndChild(colorTransparentNodeText);
  1020. // ---------------------------
  1021. TiXmlElement* transparencyNode = new TiXmlElement("transparency");
  1022. blinnNode->LinkEndChild(transparencyNode);
  1023. TiXmlElement* floatTransparencyNode = new TiXmlElement("float");
  1024. transparencyNode->LinkEndChild(floatTransparencyNode);
  1025. floatTransparencyNode->SetAttribute("sid", "transparency");
  1026. TiXmlText* floatTransparencyText = new TiXmlText("0.0");
  1027. floatTransparencyNode->LinkEndChild(floatTransparencyText);
  1028. // ---------------------------
  1029. TiXmlElement* refractionNode = new TiXmlElement("index_of_refraction");
  1030. blinnNode->LinkEndChild(refractionNode);
  1031. TiXmlElement* colorRefractionNode = new TiXmlElement("float");
  1032. refractionNode->LinkEndChild(colorRefractionNode);
  1033. colorRefractionNode->SetAttribute("sid", "index_of_refraction");
  1034. TiXmlText* colorRefractionNodeText = new TiXmlText("1");
  1035. colorRefractionNode->LinkEndChild(colorRefractionNodeText);
  1036. }
  1037. // Finally the material library
  1038. TiXmlElement* matLibNode = new TiXmlElement("library_materials");
  1039. rootNode->LinkEndChild(matLibNode);
  1040. for (U32 i = 0; i < mesh.mMaterialList.size(); i++)
  1041. {
  1042. BaseMatInstance* baseInst = mesh.mMaterialList[i];
  1043. Material* mat = dynamic_cast<Material*>(baseInst->getMaterial());
  1044. if (!mat)
  1045. continue;
  1046. TiXmlElement* materialNode = new TiXmlElement("material");
  1047. matLibNode->LinkEndChild(materialNode);
  1048. materialNode->SetAttribute("id", avar("%s-material", matNames[i].c_str()));
  1049. materialNode->SetAttribute("name", matNames[i].c_str());
  1050. TiXmlElement* instEffectNode = new TiXmlElement("instance_effect");
  1051. materialNode->LinkEndChild(instEffectNode);
  1052. instEffectNode->SetAttribute("url", avar("#%s-effect", matNames[i].c_str()));
  1053. }
  1054. }
  1055. void ColladaUtils::exportColladaTriangles(TiXmlElement* meshNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames)
  1056. {
  1057. // Start at -1 so we will export polygons that do not have a material.
  1058. for (S32 i = -1; i < matNames.size(); i++)
  1059. {
  1060. // Calculate the number of triangles that uses this Material
  1061. U32 triangleCount = 0;
  1062. for (U32 j = 0; j < mesh.mPolyList.size(); j++)
  1063. {
  1064. const OptimizedPolyList::Poly& poly = mesh.mPolyList[j];
  1065. if (poly.material != i)
  1066. continue;
  1067. if (poly.vertexCount < 3)
  1068. continue;
  1069. if (poly.type == OptimizedPolyList::TriangleList ||
  1070. poly.type == OptimizedPolyList::TriangleFan ||
  1071. poly.type == OptimizedPolyList::TriangleStrip)
  1072. {
  1073. triangleCount += poly.vertexCount - 2;
  1074. }
  1075. else
  1076. AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!");
  1077. }
  1078. // Make sure that we are actually using this Material
  1079. if (triangleCount == 0)
  1080. continue;
  1081. TiXmlElement* trianglesNode = new TiXmlElement("triangles");
  1082. meshNode->LinkEndChild(trianglesNode);
  1083. trianglesNode->SetAttribute("material", ( i > -1 ) ? avar("%s-material", matNames[i].c_str()) : "" );
  1084. trianglesNode->SetAttribute("count", avar("%d", triangleCount));
  1085. TiXmlElement* trianglesVertInputNode = new TiXmlElement("input");
  1086. trianglesNode->LinkEndChild(trianglesVertInputNode);
  1087. trianglesVertInputNode->SetAttribute("semantic", "VERTEX");
  1088. trianglesVertInputNode->SetAttribute("source", avar("#%s-mesh-vertices", meshName.c_str()));
  1089. trianglesVertInputNode->SetAttribute("offset", "0");
  1090. TiXmlElement* trianglesNormalInputNode = new TiXmlElement("input");
  1091. trianglesNode->LinkEndChild(trianglesNormalInputNode);
  1092. trianglesNormalInputNode->SetAttribute("semantic", "NORMAL");
  1093. trianglesNormalInputNode->SetAttribute("source", avar("#%s-mesh-normals", meshName.c_str()));
  1094. trianglesNormalInputNode->SetAttribute("offset", "1");
  1095. TiXmlElement* trianglesUV0InputNode = new TiXmlElement("input");
  1096. trianglesNode->LinkEndChild(trianglesUV0InputNode);
  1097. trianglesUV0InputNode->SetAttribute("semantic", "TEXCOORD");
  1098. trianglesUV0InputNode->SetAttribute("source", avar("#%s-mesh-map-0", meshName.c_str()));
  1099. trianglesUV0InputNode->SetAttribute("offset", "2");
  1100. trianglesUV0InputNode->SetAttribute("set", "0");
  1101. TiXmlElement* polyNode = new TiXmlElement("p");
  1102. trianglesNode->LinkEndChild(polyNode);
  1103. Vector<U32> tempIndices;
  1104. tempIndices.reserve(4);
  1105. for (U32 j = 0; j < mesh.mPolyList.size(); j++)
  1106. {
  1107. const OptimizedPolyList::Poly& poly = mesh.mPolyList[j];
  1108. if (poly.vertexCount < 3)
  1109. continue;
  1110. if (poly.material != i)
  1111. continue;
  1112. tempIndices.setSize(poly.vertexCount);
  1113. dMemset(tempIndices.address(), 0, poly.vertexCount);
  1114. if (poly.type == OptimizedPolyList::TriangleStrip)
  1115. {
  1116. tempIndices[0] = 0;
  1117. U32 idx = 1;
  1118. for (U32 k = 1; k < poly.vertexCount; k += 2)
  1119. tempIndices[idx++] = k;
  1120. for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2)
  1121. tempIndices[idx++] = k;
  1122. }
  1123. else if (poly.type == OptimizedPolyList::TriangleList ||
  1124. poly.type == OptimizedPolyList::TriangleFan)
  1125. {
  1126. for (U32 k = 0; k < poly.vertexCount; k++)
  1127. tempIndices[k] = k;
  1128. }
  1129. else
  1130. AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!");
  1131. const U32& firstIdx = mesh.mIndexList[poly.vertexStart];
  1132. const OptimizedPolyList::VertIndex& firstVertIdx = mesh.mVertexList[firstIdx];
  1133. for (U32 k = 1; k < poly.vertexCount - 1; k++)
  1134. {
  1135. const U32& secondIdx = mesh.mIndexList[poly.vertexStart + tempIndices[k]];
  1136. const U32& thirdIdx = mesh.mIndexList[poly.vertexStart + tempIndices[k + 1]];
  1137. const OptimizedPolyList::VertIndex& secondVertIdx = mesh.mVertexList[secondIdx];
  1138. const OptimizedPolyList::VertIndex& thirdVertIdx = mesh.mVertexList[thirdIdx];
  1139. // Note the reversed winding on the triangles
  1140. const char* tri = avar("%d %d %d %d %d %d %d %d %d ",
  1141. thirdVertIdx.vertIdx, thirdVertIdx.normalIdx, thirdVertIdx.uv0Idx,
  1142. secondVertIdx.vertIdx, secondVertIdx.normalIdx, secondVertIdx.uv0Idx,
  1143. firstVertIdx.vertIdx, firstVertIdx.normalIdx, firstVertIdx.uv0Idx);
  1144. TiXmlText* triangleText = new TiXmlText(tri);
  1145. polyNode->LinkEndChild(triangleText);
  1146. }
  1147. }
  1148. }
  1149. }
  1150. void ColladaUtils::exportColladaMesh(TiXmlElement* rootNode, const OptimizedPolyList& mesh, const String& meshName, const Vector<String>& matNames)
  1151. {
  1152. TiXmlElement* libGeomsNode = new TiXmlElement("library_geometries");
  1153. rootNode->LinkEndChild(libGeomsNode);
  1154. TiXmlElement* geometryNode = new TiXmlElement("geometry");
  1155. libGeomsNode->LinkEndChild(geometryNode);
  1156. geometryNode->SetAttribute("id", avar("%s-mesh", meshName.c_str()));
  1157. geometryNode->SetAttribute("name", avar("%s", meshName.c_str()));
  1158. TiXmlElement* meshNode = new TiXmlElement("mesh");
  1159. geometryNode->LinkEndChild(meshNode);
  1160. // Save out the vertices
  1161. TiXmlElement* vertsSourceNode = new TiXmlElement("source");
  1162. meshNode->LinkEndChild(vertsSourceNode);
  1163. vertsSourceNode->SetAttribute("id", avar("%s-mesh-positions", meshName.c_str()));
  1164. TiXmlElement* vertsNode = new TiXmlElement("float_array");
  1165. vertsSourceNode->LinkEndChild(vertsNode);
  1166. vertsNode->SetAttribute("id", avar("%s-mesh-positions-array", meshName.c_str()));
  1167. vertsNode->SetAttribute("count", avar("%d", mesh.mPoints.size() * 3));
  1168. for (U32 i = 0; i < mesh.mPoints.size(); i++)
  1169. {
  1170. const Point3F& vert = mesh.mPoints[i];
  1171. TiXmlText* vertText = new TiXmlText(avar("%.4f %.4f %.4f ", vert.x, vert.y, vert.z));
  1172. vertsNode->LinkEndChild(vertText);
  1173. }
  1174. // Save the vertex accessor
  1175. TiXmlElement* vertsTechNode = new TiXmlElement("technique_common");
  1176. vertsSourceNode->LinkEndChild(vertsTechNode);
  1177. TiXmlElement* vertsAccNode = new TiXmlElement("accessor");
  1178. vertsTechNode->LinkEndChild(vertsAccNode);
  1179. vertsAccNode->SetAttribute("source", avar("#%s-mesh-positions-array", meshName.c_str()));
  1180. vertsAccNode->SetAttribute("count", avar("%d", mesh.mPoints.size()));
  1181. vertsAccNode->SetAttribute("stride", "3");
  1182. TiXmlElement* vertsAccXNode = new TiXmlElement("param");
  1183. vertsAccNode->LinkEndChild(vertsAccXNode);
  1184. vertsAccXNode->SetAttribute("name", "X");
  1185. vertsAccXNode->SetAttribute("type", "float");
  1186. TiXmlElement* vertsAccYNode = new TiXmlElement("param");
  1187. vertsAccNode->LinkEndChild(vertsAccYNode);
  1188. vertsAccYNode->SetAttribute("name", "Y");
  1189. vertsAccYNode->SetAttribute("type", "float");
  1190. TiXmlElement* vertsAccZNode = new TiXmlElement("param");
  1191. vertsAccNode->LinkEndChild(vertsAccZNode);
  1192. vertsAccZNode->SetAttribute("name", "Z");
  1193. vertsAccZNode->SetAttribute("type", "float");
  1194. // Save out the normals
  1195. TiXmlElement* normalsSourceNode = new TiXmlElement("source");
  1196. meshNode->LinkEndChild(normalsSourceNode);
  1197. normalsSourceNode->SetAttribute("id", avar("%s-mesh-normals", meshName.c_str()));
  1198. TiXmlElement* normalsNode = new TiXmlElement("float_array");
  1199. normalsSourceNode->LinkEndChild(normalsNode);
  1200. normalsNode->SetAttribute("id", avar("%s-mesh-normals-array", meshName.c_str()));
  1201. normalsNode->SetAttribute("count", avar("%d", mesh.mNormals.size() * 3));
  1202. for (U32 i = 0; i < mesh.mNormals.size(); i++)
  1203. {
  1204. const Point3F& normal = mesh.mNormals[i];
  1205. TiXmlText* normalText = new TiXmlText(avar("%.4f %.4f %.4f ", normal.x, normal.y, normal.z));
  1206. normalsNode->LinkEndChild(normalText);
  1207. }
  1208. // Save the normals accessor
  1209. TiXmlElement* normalsTechNode = new TiXmlElement("technique_common");
  1210. normalsSourceNode->LinkEndChild(normalsTechNode);
  1211. TiXmlElement* normalsAccNode = new TiXmlElement("accessor");
  1212. normalsTechNode->LinkEndChild(normalsAccNode);
  1213. normalsAccNode->SetAttribute("source", avar("#%s-mesh-normals-array", meshName.c_str()));
  1214. normalsAccNode->SetAttribute("count", avar("%d", mesh.mNormals.size()));
  1215. normalsAccNode->SetAttribute("stride", "3");
  1216. TiXmlElement* normalsAccXNode = new TiXmlElement("param");
  1217. normalsAccNode->LinkEndChild(normalsAccXNode);
  1218. normalsAccXNode->SetAttribute("name", "X");
  1219. normalsAccXNode->SetAttribute("type", "float");
  1220. TiXmlElement* normalsAccYNode = new TiXmlElement("param");
  1221. normalsAccNode->LinkEndChild(normalsAccYNode);
  1222. normalsAccYNode->SetAttribute("name", "Y");
  1223. normalsAccYNode->SetAttribute("type", "float");
  1224. TiXmlElement* normalsAccZNode = new TiXmlElement("param");
  1225. normalsAccNode->LinkEndChild(normalsAccZNode);
  1226. normalsAccZNode->SetAttribute("name", "Z");
  1227. normalsAccZNode->SetAttribute("type", "float");
  1228. // Save out the uvs
  1229. TiXmlElement* uv0SourceNode = new TiXmlElement("source");
  1230. meshNode->LinkEndChild(uv0SourceNode);
  1231. uv0SourceNode->SetAttribute("id", avar("%s-mesh-map-0", meshName.c_str()));
  1232. TiXmlElement* uv0Node = new TiXmlElement("float_array");
  1233. uv0SourceNode->LinkEndChild(uv0Node);
  1234. uv0Node->SetAttribute("id", avar("%s-mesh-map-0-array", meshName.c_str()));
  1235. uv0Node->SetAttribute("count", avar("%d", mesh.mUV0s.size() * 2));
  1236. for (U32 i = 0; i < mesh.mUV0s.size(); i++)
  1237. {
  1238. const Point2F& uv0 = mesh.mUV0s[i];
  1239. TiXmlText* uv0Text = new TiXmlText(avar("%.4f %.4f ", uv0.x, 1.0f - uv0.y)); // COLLADA uvs are upside down compared to Torque
  1240. uv0Node->LinkEndChild(uv0Text);
  1241. }
  1242. // Save the uv0 accessor
  1243. TiXmlElement* uv0TechNode = new TiXmlElement("technique_common");
  1244. uv0SourceNode->LinkEndChild(uv0TechNode);
  1245. TiXmlElement* uv0AccNode = new TiXmlElement("accessor");
  1246. uv0TechNode->LinkEndChild(uv0AccNode);
  1247. uv0AccNode->SetAttribute("source", avar("#%s-mesh-map-0-array", meshName.c_str()));
  1248. uv0AccNode->SetAttribute("count", avar("%d", mesh.mUV0s.size()));
  1249. uv0AccNode->SetAttribute("stride", "2");
  1250. TiXmlElement* uv0AccSNode = new TiXmlElement("param");
  1251. uv0AccNode->LinkEndChild(uv0AccSNode);
  1252. uv0AccSNode->SetAttribute("name", "S");
  1253. uv0AccSNode->SetAttribute("type", "float");
  1254. TiXmlElement* uv0AccTNode = new TiXmlElement("param");
  1255. uv0AccNode->LinkEndChild(uv0AccTNode);
  1256. uv0AccTNode->SetAttribute("name", "T");
  1257. uv0AccTNode->SetAttribute("type", "float");
  1258. // Define the vertices position array
  1259. TiXmlElement* verticesNode = new TiXmlElement("vertices");
  1260. meshNode->LinkEndChild(verticesNode);
  1261. verticesNode->SetAttribute("id", avar("%s-mesh-vertices", meshName.c_str()));
  1262. TiXmlElement* verticesInputNode = new TiXmlElement("input");
  1263. verticesNode->LinkEndChild(verticesInputNode);
  1264. verticesInputNode->SetAttribute("semantic", "POSITION");
  1265. verticesInputNode->SetAttribute("source", avar("#%s-mesh-positions", meshName.c_str()));
  1266. exportColladaTriangles(meshNode, mesh, meshName, matNames);
  1267. }
  1268. void ColladaUtils::exportColladaScene(TiXmlElement* rootNode, const String& meshName, const Vector<String>& matNames)
  1269. {
  1270. TiXmlElement* libSceneNode = new TiXmlElement("library_visual_scenes");
  1271. rootNode->LinkEndChild(libSceneNode);
  1272. TiXmlElement* visSceneNode = new TiXmlElement("visual_scene");
  1273. libSceneNode->LinkEndChild(visSceneNode);
  1274. visSceneNode->SetAttribute("id", "RootNode");
  1275. visSceneNode->SetAttribute("name", "RootNode");
  1276. TiXmlElement* nodeNode = new TiXmlElement("node");
  1277. visSceneNode->LinkEndChild(nodeNode);
  1278. nodeNode->SetAttribute("id", avar("%s", meshName.c_str()));
  1279. nodeNode->SetAttribute("name", avar("%s", meshName.c_str()));
  1280. nodeNode->SetAttribute("type", "NODE");
  1281. TiXmlElement* instanceGeomNode = new TiXmlElement("instance_geometry");
  1282. nodeNode->LinkEndChild(instanceGeomNode);
  1283. instanceGeomNode->SetAttribute("url", avar("#%s-mesh", meshName.c_str()));
  1284. instanceGeomNode->SetAttribute("name", avar("%s", meshName.c_str()));
  1285. TiXmlElement* bindMatNode = new TiXmlElement("bind_material");
  1286. instanceGeomNode->LinkEndChild(bindMatNode);
  1287. TiXmlElement* techniqueNode = new TiXmlElement("technique_common");
  1288. bindMatNode->LinkEndChild(techniqueNode);
  1289. // Bind the materials
  1290. for (U32 i = 0; i < matNames.size(); i++)
  1291. {
  1292. TiXmlElement* instMatNode = new TiXmlElement("instance_material");
  1293. techniqueNode->LinkEndChild(instMatNode);
  1294. instMatNode->SetAttribute("symbol", avar("%s-material", matNames[i].c_str()));
  1295. instMatNode->SetAttribute("target", avar("#%s-material", matNames[i].c_str()));
  1296. TiXmlElement* bindVertexNode = new TiXmlElement("bind_vertex_input");
  1297. instMatNode->LinkEndChild(bindVertexNode);
  1298. //bindVertexNode->SetAttribute("semantic", avar("%s-mesh-map-0", meshName.c_str()));
  1299. bindVertexNode->SetAttribute("semantic", "UVMap");
  1300. bindVertexNode->SetAttribute("input_semantic", "TEXCOORD");
  1301. bindVertexNode->SetAttribute("input_set", "0");
  1302. }
  1303. // Extra info useful for COLLADAMax importer (OpenCOLLADA)
  1304. TiXmlElement* extraInsGeoNode = new TiXmlElement("extra");
  1305. nodeNode->LinkEndChild(extraInsGeoNode);
  1306. TiXmlElement* extraInsGeoTechNode = new TiXmlElement("technique");
  1307. extraInsGeoNode->LinkEndChild(extraInsGeoTechNode);
  1308. extraInsGeoTechNode->SetAttribute("profile", "OpenCOLLADA");
  1309. TiXmlElement* castShadowsNode = new TiXmlElement("cast_shadows");
  1310. extraInsGeoTechNode->LinkEndChild(castShadowsNode);
  1311. castShadowsNode->SetAttribute("sid", "cast_shadows");
  1312. castShadowsNode->SetAttribute("type", "bool");
  1313. TiXmlText* castShadowsText = new TiXmlText("1");
  1314. castShadowsNode->LinkEndChild(castShadowsText);
  1315. //-----------------------------
  1316. TiXmlElement* receiveShadowsNode = new TiXmlElement("receive_shadows");
  1317. extraInsGeoTechNode->LinkEndChild(receiveShadowsNode);
  1318. receiveShadowsNode->SetAttribute("sid", "receive_shadows");
  1319. receiveShadowsNode->SetAttribute("type", "bool");
  1320. TiXmlText* receiveShadowsText = new TiXmlText("1");
  1321. receiveShadowsNode->LinkEndChild(receiveShadowsText);
  1322. //-----------------------------
  1323. TiXmlElement* primaryVisibiltyNode = new TiXmlElement("primary_visibility");
  1324. extraInsGeoTechNode->LinkEndChild(primaryVisibiltyNode);
  1325. primaryVisibiltyNode->SetAttribute("sid", "primary_visibility");
  1326. primaryVisibiltyNode->SetAttribute("type", "int");
  1327. TiXmlText* primaryVisibiltyText = new TiXmlText("1");
  1328. primaryVisibiltyNode->LinkEndChild(primaryVisibiltyText);
  1329. //-----------------------------
  1330. TiXmlElement* secondaryVisibilityNode = new TiXmlElement("secondary_visibility");
  1331. extraInsGeoTechNode->LinkEndChild(secondaryVisibilityNode);
  1332. secondaryVisibilityNode->SetAttribute("sid", "secondary_visibility");
  1333. secondaryVisibilityNode->SetAttribute("type", "int");
  1334. TiXmlText* secondaryVisibilityText = new TiXmlText("1");
  1335. secondaryVisibilityNode->LinkEndChild(secondaryVisibilityText);
  1336. // Extra info useful for COLLADAMaya importer (OpenCOLLADA)
  1337. TiXmlElement* extra2InsGeoNode = new TiXmlElement("extra");
  1338. nodeNode->LinkEndChild(extra2InsGeoNode);
  1339. TiXmlElement* extra2InsGeoTechNode = new TiXmlElement("technique");
  1340. extra2InsGeoNode->LinkEndChild(extra2InsGeoTechNode);
  1341. extra2InsGeoTechNode->SetAttribute("profile", "OpenCOLLADAMaya");
  1342. TiXmlElement* mayaNodeId = new TiXmlElement("originalMayaNodeId");
  1343. extra2InsGeoTechNode->LinkEndChild(mayaNodeId);
  1344. mayaNodeId->SetAttribute("sid", "originalMayaNodeId");
  1345. mayaNodeId->SetAttribute("type", "string");
  1346. TiXmlText* mayaNodeIdMesh = new TiXmlText(avar("%s", meshName.c_str()));
  1347. mayaNodeId->LinkEndChild(mayaNodeIdMesh);
  1348. TiXmlElement* paramExtraNode = new TiXmlElement("param");
  1349. extra2InsGeoTechNode->LinkEndChild(paramExtraNode);
  1350. paramExtraNode->SetAttribute("sid", "colladaId");
  1351. paramExtraNode->SetAttribute("type", "string");
  1352. TiXmlText* mayaParamMesh = new TiXmlText(avar("%s", meshName.c_str()));
  1353. paramExtraNode->LinkEndChild(mayaParamMesh);
  1354. TiXmlElement* sceneNode = new TiXmlElement("scene");
  1355. rootNode->LinkEndChild(sceneNode);
  1356. TiXmlElement* instVisSceneNode = new TiXmlElement("instance_visual_scene");
  1357. sceneNode->LinkEndChild(instVisSceneNode);
  1358. instVisSceneNode->SetAttribute("url", "#RootNode");
  1359. }
  1360. void ColladaUtils::exportToCollada(const Torque::Path& colladaFile, const OptimizedPolyList& mesh, const String& meshName)
  1361. {
  1362. // Get the mesh name
  1363. String outMeshName = meshName;
  1364. if (outMeshName.isEmpty())
  1365. outMeshName = colladaFile.getFileName();
  1366. // The XML document that will hold all of our data
  1367. TiXmlDocument doc;
  1368. // Add a standard XML declaration to the top
  1369. TiXmlDeclaration* xmlDecl = new TiXmlDeclaration("1.0", "utf-8", "");
  1370. doc.LinkEndChild(xmlDecl);
  1371. // Create our Collada root node and populate a couple standard attributes
  1372. TiXmlElement* rootNode = new TiXmlElement("COLLADA");
  1373. rootNode->SetAttribute("xmlns", "http://www.collada.org/2005/11/COLLADASchema");
  1374. rootNode->SetAttribute("version", "1.4.1");
  1375. rootNode->SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
  1376. // Add the root node to the document
  1377. doc.LinkEndChild(rootNode);
  1378. // Save out our header info
  1379. exportColladaHeader(rootNode);
  1380. // Save out the materials
  1381. Vector<String> mapNames;
  1382. exportColladaMaterials(rootNode, mesh, mapNames, colladaFile);
  1383. // Save out our geometry
  1384. exportColladaMesh(rootNode, mesh, outMeshName, mapNames);
  1385. // Save out our scene nodes
  1386. exportColladaScene(rootNode, outMeshName, mapNames);
  1387. // Write out the actual Collada file
  1388. char fullPath[MAX_PATH_LENGTH];
  1389. Platform::makeFullPathName(colladaFile.getFullPath(), fullPath, MAX_PATH_LENGTH);
  1390. if (!doc.SaveFile(fullPath))
  1391. Con::errorf("ColladaUtils::exportToCollada(): Unable to export to %s", fullPath);
  1392. }