ComputeUVMappingProcess.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. /*
  2. Open Asset Import Library (ASSIMP)
  3. ----------------------------------------------------------------------
  4. Copyright (c) 2006-2008, ASSIMP Development Team
  5. All rights reserved.
  6. Redistribution and use of this software in source and binary forms,
  7. with or without modification, are permitted provided that the
  8. following conditions are met:
  9. * Redistributions of source code must retain the above
  10. copyright notice, this list of conditions and the
  11. following disclaimer.
  12. * Redistributions in binary form must reproduce the above
  13. copyright notice, this list of conditions and the
  14. following disclaimer in the documentation and/or other
  15. materials provided with the distribution.
  16. * Neither the name of the ASSIMP team, nor the names of its
  17. contributors may be used to endorse or promote products
  18. derived from this software without specific prior
  19. written permission of the ASSIMP Development Team.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. ----------------------------------------------------------------------
  32. */
  33. /** @file GenUVCoords step */
  34. #include "AssimpPCH.h"
  35. #include "ComputeUVMappingProcess.h"
  36. #include "ProcessHelper.h"
  37. using namespace Assimp;
  38. namespace {
  39. const static aiVector3D base_axis_y(0.f,1.f,0.f);
  40. const static aiVector3D base_axis_x(1.f,0.f,0.f);
  41. const static aiVector3D base_axis_z(0.f,0.f,1.f);
  42. const static float angle_epsilon = 0.95f;
  43. }
  44. // ------------------------------------------------------------------------------------------------
  45. // Constructor to be privately used by Importer
  46. ComputeUVMappingProcess::ComputeUVMappingProcess()
  47. {
  48. // nothing to do here
  49. }
  50. // ------------------------------------------------------------------------------------------------
  51. // Destructor, private as well
  52. ComputeUVMappingProcess::~ComputeUVMappingProcess()
  53. {
  54. // nothing to do here
  55. }
  56. // ------------------------------------------------------------------------------------------------
  57. // Returns whether the processing step is present in the given flag field.
  58. bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const
  59. {
  60. return (pFlags & aiProcess_GenUVCoords) != 0;
  61. }
  62. // ------------------------------------------------------------------------------------------------
  63. // Check whether a ray intersects a plane and find the intersection point
  64. inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
  65. const aiVector3D& planeNormal, aiVector3D& pos)
  66. {
  67. const float b = planeNormal * (planePos - ray.pos);
  68. float h = ray.dir * planeNormal;
  69. if (h < 10e-5f && h > -10e-5f || (h = b/h) < 0)
  70. return false;
  71. pos = ray.pos + (ray.dir * h);
  72. return true;
  73. }
  74. // ------------------------------------------------------------------------------------------------
  75. // Find the first empty UV channel in a mesh
  76. inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
  77. {
  78. for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m)
  79. if (!mesh->mTextureCoords[m])return m;
  80. DefaultLogger::get()->error("Unable to compute UV coordinates, no free UV slot found");
  81. return 0xffffffff;
  82. }
  83. // ------------------------------------------------------------------------------------------------
  84. // Try to remove UV seams
  85. void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
  86. {
  87. // TODO: just a very rough algorithm. I think it could be done
  88. // much easier, but I don't know how and am currently too tired to
  89. // to think about a better solution.
  90. const static float LOWER_LIMIT = 0.1f;
  91. const static float UPPER_LIMIT = 0.9f;
  92. const static float LOWER_EPSILON = 10e-3f;
  93. const static float UPPER_EPSILON = 1.f-10e-3f;
  94. for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx)
  95. {
  96. const aiFace& face = mesh->mFaces[fidx];
  97. if (face.mNumIndices < 3) continue; // triangles and polygons only, please
  98. unsigned int small = face.mNumIndices, large = small;
  99. bool zero = false, one = false, round_to_zero = false;
  100. // Check whether this face lies on a UV seam. We can just guess,
  101. // but the assumption that a face with at least one very small
  102. // on the one side and one very large U coord on the other side
  103. // lies on a UV seam should work for most cases.
  104. for (unsigned int n = 0; n < face.mNumIndices;++n)
  105. {
  106. if (out[face.mIndices[n]].x < LOWER_LIMIT)
  107. {
  108. small = n;
  109. // If we have a U value very close to 0 we can't
  110. // round the others to 0, too.
  111. if (out[face.mIndices[n]].x <= LOWER_EPSILON)
  112. zero = true;
  113. else round_to_zero = true;
  114. }
  115. if (out[face.mIndices[n]].x > UPPER_LIMIT)
  116. {
  117. large = n;
  118. // If we have a U value very close to 1 we can't
  119. // round the others to 1, too.
  120. if (out[face.mIndices[n]].x >= UPPER_EPSILON)
  121. one = true;
  122. }
  123. }
  124. if (small != face.mNumIndices && large != face.mNumIndices)
  125. {
  126. for (unsigned int n = 0; n < face.mNumIndices;++n)
  127. {
  128. // If the u value is over the upper limit and no other u
  129. // value of that face is 0, round it to 0
  130. if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
  131. out[face.mIndices[n]].x = 0.f;
  132. // If the u value is below the lower limit and no other u
  133. // value of that face is 1, round it to 1
  134. else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one)
  135. out[face.mIndices[n]].x = 1.f;
  136. // The face contains both 0 and 1 as UV coords. This can occur
  137. // for faces which have an edge that lies directly on the seam.
  138. // Due to numerical inaccuracies one U coord becomes 0, the
  139. // other 1. But we do still have a third UV coord to determine
  140. // to which side we must round to.
  141. else if (one && zero)
  142. {
  143. if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
  144. out[face.mIndices[n]].x = 0.f;
  145. else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
  146. out[face.mIndices[n]].x = 1.f;
  147. }
  148. }
  149. }
  150. }
  151. }
  152. // ------------------------------------------------------------------------------------------------
  153. void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
  154. {
  155. aiVector3D center, min, max;
  156. // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
  157. // currently the mapping axis will always be one of x,y,z, except if the
  158. // PretransformVertices step is used (it transforms the meshes into worldspace,
  159. // thus changing the mapping axis)
  160. if (axis * base_axis_x >= angle_epsilon) {
  161. FindMeshCenter(mesh, center, min, max);
  162. // For each point get a normalized projection vector in the sphere,
  163. // get its longitude and latitude and map them to their respective
  164. // UV axes. Problems occur around the poles ... unsolvable.
  165. //
  166. // The spherical coordinate system looks like this:
  167. // x = cos(lon)*cos(lat)
  168. // y = sin(lon)*cos(lat)
  169. // z = sin(lat)
  170. //
  171. // Thus we can derive:
  172. // lat = arcsin (z)
  173. // lon = arctan (y/x)
  174. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)
  175. {
  176. const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
  177. out[pnt] = aiVector3D((atan2 (diff.z, diff.y) + (float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI,
  178. (asin (diff.x) + (float)AI_MATH_HALF_PI) / (float)AI_MATH_PI, 0.f);
  179. }
  180. }
  181. else if (axis * base_axis_y >= angle_epsilon) {
  182. FindMeshCenter(mesh, center, min, max);
  183. // ... just the same again
  184. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)
  185. {
  186. const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
  187. out[pnt] = aiVector3D((atan2 (diff.x, diff.z) + (float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI,
  188. (asin (diff.y) + (float)AI_MATH_HALF_PI) / (float)AI_MATH_PI, 0.f);
  189. }
  190. }
  191. else if (axis * base_axis_z >= angle_epsilon) {
  192. FindMeshCenter(mesh, center, min, max);
  193. // ... just the same again
  194. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)
  195. {
  196. const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
  197. out[pnt] = aiVector3D((atan2 (diff.y, diff.x) + (float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI,
  198. (asin (diff.z) + (float)AI_MATH_HALF_PI) / (float)AI_MATH_PI, 0.f);
  199. }
  200. }
  201. // slower code path in case the mapping axis is not one of the coordinate system axes
  202. else
  203. {
  204. aiMatrix4x4 mTrafo;
  205. aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
  206. FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
  207. // again the same, except we're applying a transformation now
  208. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)
  209. {
  210. const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize();
  211. out[pnt] = aiVector3D((atan2 (diff.y, diff.x) + (float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI,
  212. (asin (diff.z) + (float)AI_MATH_HALF_PI) / (float)AI_MATH_PI, 0.f);
  213. }
  214. }
  215. // Now find and remove UV seams. A seam occurs if a face has a tcoord
  216. // close to zero on the one side, and a tcoord close to one on the
  217. // other side.
  218. RemoveUVSeams(mesh,out);
  219. }
  220. // ------------------------------------------------------------------------------------------------
  221. void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
  222. {
  223. aiVector3D center, min, max;
  224. // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
  225. // currently the mapping axis will always be one of x,y,z, except if the
  226. // PretransformVertices step is used (it transforms the meshes into worldspace,
  227. // thus changing the mapping axis)
  228. if (axis * base_axis_x >= angle_epsilon) {
  229. FindMeshCenter(mesh, center, min, max);
  230. const float diff = max.x - min.x;
  231. // If the main axis is 'z', the z coordinate of a point 'p' is mapped
  232. // directly to the texture V axis. The other axis is derived from
  233. // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
  234. // 'c' is the center point of the mesh.
  235. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
  236. const aiVector3D& pos = mesh->mVertices[pnt];
  237. aiVector3D& uv = out[pnt];
  238. uv.y = (pos.x - min.x) / diff;
  239. uv.x = (atan2 ( pos.z - center.z, pos.y - center.y) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
  240. }
  241. }
  242. else if (axis * base_axis_y >= angle_epsilon) {
  243. FindMeshCenter(mesh, center, min, max);
  244. const float diff = max.y - min.y;
  245. // just the same ...
  246. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
  247. const aiVector3D& pos = mesh->mVertices[pnt];
  248. aiVector3D& uv = out[pnt];
  249. uv.y = (pos.y - min.y) / diff;
  250. uv.x = (atan2 ( pos.x - center.x, pos.z - center.z) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
  251. }
  252. }
  253. else if (axis * base_axis_z >= angle_epsilon) {
  254. FindMeshCenter(mesh, center, min, max);
  255. const float diff = max.z - min.z;
  256. // just the same ...
  257. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
  258. const aiVector3D& pos = mesh->mVertices[pnt];
  259. aiVector3D& uv = out[pnt];
  260. uv.y = (pos.z - min.z) / diff;
  261. uv.x = (atan2 ( pos.y - center.y, pos.x - center.x) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
  262. }
  263. }
  264. // slower code path in case the mapping axis is not one of the coordinate system axes
  265. else {
  266. aiMatrix4x4 mTrafo;
  267. aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
  268. FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
  269. const float diff = max.y - min.y;
  270. // again the same, except we're applying a transformation now
  271. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){
  272. const aiVector3D pos = mTrafo* mesh->mVertices[pnt];
  273. aiVector3D& uv = out[pnt];
  274. uv.y = (pos.y - min.y) / diff;
  275. uv.x = (atan2 ( pos.x - center.x, pos.z - center.z) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
  276. }
  277. }
  278. // Now find and remove UV seams. A seam occurs if a face has a tcoord
  279. // close to zero on the one side, and a tcoord close to one on the
  280. // other side.
  281. RemoveUVSeams(mesh,out);
  282. }
  283. // ------------------------------------------------------------------------------------------------
  284. void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
  285. {
  286. float diffu,diffv;
  287. aiVector3D center, min, max;
  288. // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
  289. // currently the mapping axis will always be one of x,y,z, except if the
  290. // PretransformVertices step is used (it transforms the meshes into worldspace,
  291. // thus changing the mapping axis)
  292. if (axis * base_axis_x >= angle_epsilon) {
  293. FindMeshCenter(mesh, center, min, max);
  294. diffu = max.z - min.z;
  295. diffv = max.y - min.y;
  296. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
  297. const aiVector3D& pos = mesh->mVertices[pnt];
  298. out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv);
  299. }
  300. }
  301. else if (axis * base_axis_y >= angle_epsilon) {
  302. FindMeshCenter(mesh, center, min, max);
  303. diffu = max.x - min.x;
  304. diffv = max.z - min.z;
  305. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
  306. const aiVector3D& pos = mesh->mVertices[pnt];
  307. out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv);
  308. }
  309. }
  310. else if (axis * base_axis_z >= angle_epsilon) {
  311. FindMeshCenter(mesh, center, min, max);
  312. diffu = max.y - min.y;
  313. diffv = max.z - min.z;
  314. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
  315. const aiVector3D& pos = mesh->mVertices[pnt];
  316. out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv);
  317. }
  318. }
  319. // slower code path in case the mapping axis is not one of the coordinate system axes
  320. else
  321. {
  322. aiMatrix4x4 mTrafo;
  323. aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
  324. FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
  325. diffu = max.x - min.x;
  326. diffv = max.z - min.z;
  327. // again the same, except we're applying a transformation now
  328. for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
  329. const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
  330. out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv);
  331. }
  332. }
  333. // shouldn't be necessary to remove UV seams ...
  334. }
  335. // ------------------------------------------------------------------------------------------------
  336. void ComputeUVMappingProcess::ComputeBoxMapping(aiMesh* mesh, aiVector3D* out)
  337. {
  338. DefaultLogger::get()->error("Mapping type currently not implemented");
  339. }
  340. // ------------------------------------------------------------------------------------------------
  341. void ComputeUVMappingProcess::Execute( aiScene* pScene)
  342. {
  343. DefaultLogger::get()->debug("GenUVCoordsProcess begin");
  344. char buffer[1024];
  345. if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
  346. throw new ImportErrorException("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
  347. std::list<MappingInfo> mappingStack;
  348. /* Iterate through all materials and search for non-UV mapped textures
  349. */
  350. for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
  351. {
  352. mappingStack.clear();
  353. aiMaterial* mat = pScene->mMaterials[i];
  354. for (unsigned int a = 0; a < mat->mNumProperties;++a)
  355. {
  356. aiMaterialProperty* prop = mat->mProperties[a];
  357. if (!::strcmp( prop->mKey.data, "$tex.mapping"))
  358. {
  359. aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData);
  360. if (aiTextureMapping_UV != mapping)
  361. {
  362. if (!DefaultLogger::isNullLogger())
  363. {
  364. sprintf(buffer, "Found non-UV mapped texture (%s,%i). Mapping type: %s",
  365. TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
  366. MappingTypeToString(mapping));
  367. DefaultLogger::get()->info(buffer);
  368. }
  369. if (aiTextureMapping_OTHER == mapping)
  370. continue;
  371. MappingInfo info (mapping);
  372. // Get further properties - currently only the major axis
  373. for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
  374. {
  375. aiMaterialProperty* prop2 = mat->mProperties[a2];
  376. if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
  377. continue;
  378. if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) {
  379. info.axis = *((aiVector3D*)prop2->mData);
  380. break;
  381. }
  382. }
  383. unsigned int idx;
  384. // Check whether we have this mapping mode already
  385. std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info);
  386. if (mappingStack.end() != it)
  387. {
  388. idx = (*it).uv;
  389. }
  390. else
  391. {
  392. /* We have found a non-UV mapped texture. Now
  393. * we need to find all meshes using this material
  394. * that we can compute UV channels for them.
  395. */
  396. for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
  397. {
  398. aiMesh* mesh = pScene->mMeshes[m];
  399. unsigned int outIdx;
  400. if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == 0xffffffff ||
  401. !mesh->mNumVertices)
  402. {
  403. continue;
  404. }
  405. // Allocate output storage
  406. aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
  407. switch (mapping)
  408. {
  409. case aiTextureMapping_SPHERE:
  410. ComputeSphereMapping(mesh,info.axis,p);
  411. break;
  412. case aiTextureMapping_CYLINDER:
  413. ComputeCylinderMapping(mesh,info.axis,p);
  414. break;
  415. case aiTextureMapping_PLANE:
  416. ComputePlaneMapping(mesh,info.axis,p);
  417. break;
  418. case aiTextureMapping_BOX:
  419. ComputeBoxMapping(mesh,p);
  420. break;
  421. default:
  422. ai_assert(false);
  423. }
  424. if (m && idx != outIdx)
  425. {
  426. DefaultLogger::get()->warn("UV index mismatch. Not all meshes assigned to "
  427. "this material have equal numbers of UV channels. The UV index stored in "
  428. "the material structure does therefore not apply for all meshes. ");
  429. }
  430. idx = outIdx;
  431. }
  432. info.uv = idx;
  433. mappingStack.push_back(info);
  434. }
  435. // Update the material property list
  436. mapping = aiTextureMapping_UV;
  437. ((MaterialHelper*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
  438. }
  439. }
  440. }
  441. }
  442. DefaultLogger::get()->debug("GenUVCoordsProcess finished");
  443. }