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