1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "BsFBXUtility.h" |
4 | #include "Math/BsVector2.h" |
5 | #include "Math/BsVector3.h" |
6 | #include "Math/BsVector4.h" |
7 | |
8 | namespace bs |
9 | { |
10 | struct SmoothNormal |
11 | { |
12 | int group = 0; |
13 | Vector3 normal = Vector3::ZERO; |
14 | |
15 | void addNormal(int group, const Vector3& normal) |
16 | { |
17 | this->group |= group; |
18 | this->normal += normal; |
19 | } |
20 | |
21 | void addNormal(const SmoothNormal& other) |
22 | { |
23 | this->group |= other.group; |
24 | this->normal += other.normal; |
25 | } |
26 | |
27 | void normalize() |
28 | { |
29 | normal.normalize(); |
30 | } |
31 | }; |
32 | |
33 | struct SmoothVertex |
34 | { |
35 | Vector<SmoothNormal> normals; |
36 | |
37 | void addNormal(int group, const Vector3& normal) |
38 | { |
39 | bool found = false; |
40 | |
41 | for (size_t i = 0; i < normals.size(); i++) |
42 | { |
43 | if ((normals[i].group & group) != 0) |
44 | { |
45 | bool otherGroups = (group & ~normals[i].group) != 0; |
46 | |
47 | if (otherGroups) |
48 | { |
49 | for (size_t j = i + 1; j < normals.size(); j++) |
50 | { |
51 | if ((normals[j].group & group) != 0) |
52 | { |
53 | normals[i].addNormal(normals[j]); |
54 | normals.erase(normals.begin() + j); |
55 | --j; |
56 | } |
57 | } |
58 | } |
59 | |
60 | normals[i].addNormal(group, normal); |
61 | |
62 | found = true; |
63 | break;; |
64 | } |
65 | } |
66 | |
67 | if (!found) |
68 | { |
69 | SmoothNormal smoothNormal; |
70 | smoothNormal.addNormal(group, normal); |
71 | |
72 | normals.push_back(smoothNormal); |
73 | } |
74 | } |
75 | |
76 | Vector3 getNormal(int group) const |
77 | { |
78 | for (size_t i = 0; i < normals.size(); i++) |
79 | { |
80 | if (normals[i].group & group) |
81 | return normals[i].normal; |
82 | } |
83 | |
84 | return Vector3::ZERO; |
85 | } |
86 | |
87 | void normalize() |
88 | { |
89 | for (size_t i = 0; i < normals.size(); ++i) |
90 | normals[i].normalize(); |
91 | } |
92 | }; |
93 | |
94 | void FBXUtility::normalsFromSmoothing(const Vector<Vector3>& positions, const Vector<int>& indices, |
95 | const Vector<int>& smoothing, Vector<Vector3>& normals) |
96 | { |
97 | std::vector<SmoothVertex> smoothNormals; |
98 | smoothNormals.resize(positions.size()); |
99 | |
100 | normals.resize(indices.size(), Vector3::ZERO); |
101 | |
102 | UINT32 numPolygons = (UINT32)(indices.size() / 3); |
103 | |
104 | int idx = 0; |
105 | for (UINT32 i = 0; i < numPolygons; i++) |
106 | { |
107 | for (UINT32 j = 0; j < 3; j++) |
108 | { |
109 | int prevOffset = (j > 0 ? j - 1 : 2); |
110 | int nextOffset = (j < 2 ? j + 1 : 0); |
111 | |
112 | int current = indices[idx + j]; |
113 | |
114 | Vector3 v0 = (Vector3)positions[indices[idx + prevOffset]]; |
115 | Vector3 v1 = (Vector3)positions[current]; |
116 | Vector3 v2 = (Vector3)positions[indices[idx + nextOffset]]; |
117 | |
118 | Vector3 normal = Vector3::cross(v0 - v1, v2 - v1); |
119 | smoothNormals[current].addNormal(smoothing[idx + j], normal); |
120 | |
121 | normals[idx + j] = normal; |
122 | } |
123 | |
124 | idx += 3; |
125 | } |
126 | |
127 | for (size_t i = 0; i < smoothNormals.size(); ++i) |
128 | smoothNormals[i].normalize(); |
129 | |
130 | idx = 0; |
131 | for (UINT32 i = 0; i < numPolygons; i++) |
132 | { |
133 | for (UINT32 j = 0; j < 3; j++) |
134 | { |
135 | if (smoothing[idx + j] != 0) |
136 | { |
137 | int current = indices[idx + j]; |
138 | |
139 | normals[idx + j] = smoothNormals[current].getNormal(smoothing[idx + j]); |
140 | } |
141 | } |
142 | |
143 | idx += 3; |
144 | } |
145 | } |
146 | |
147 | void FBXUtility::splitVertices(const FBXImportMesh& source, FBXImportMesh& dest) |
148 | { |
149 | dest.indices = source.indices; |
150 | dest.materials = source.materials; |
151 | |
152 | dest.positions = source.positions; |
153 | dest.boneInfluences = source.boneInfluences; |
154 | |
155 | // Make room for minimal set of vertices |
156 | UINT32 vertexCount = (UINT32)source.positions.size(); |
157 | if (!source.normals.empty()) |
158 | dest.normals.resize(vertexCount); |
159 | |
160 | if (!source.tangents.empty()) |
161 | dest.tangents.resize(vertexCount); |
162 | |
163 | if (!source.bitangents.empty()) |
164 | dest.bitangents.resize(vertexCount); |
165 | |
166 | if (!source.colors.empty()) |
167 | dest.colors.resize(vertexCount); |
168 | |
169 | for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++) |
170 | { |
171 | if (!source.UV[i].empty()) |
172 | dest.UV[i].resize(vertexCount); |
173 | } |
174 | |
175 | UINT32 numBlendShapes = (UINT32)source.blendShapes.size(); |
176 | dest.blendShapes.resize(numBlendShapes); |
177 | for (UINT32 i = 0; i < numBlendShapes; i++) |
178 | { |
179 | const FBXBlendShape& sourceShape = source.blendShapes[i]; |
180 | FBXBlendShape& destShape = dest.blendShapes[i]; |
181 | |
182 | UINT32 numFrames = (UINT32)sourceShape.frames.size(); |
183 | destShape.frames.resize(numFrames); |
184 | destShape.name = sourceShape.name; |
185 | |
186 | for (UINT32 j = 0; j < numFrames; j++) |
187 | { |
188 | const FBXBlendShapeFrame& sourceFrame = sourceShape.frames[j]; |
189 | FBXBlendShapeFrame& destFrame = destShape.frames[j]; |
190 | |
191 | destFrame.name = sourceFrame.name; |
192 | destFrame.weight = sourceFrame.weight; |
193 | destFrame.positions = sourceFrame.positions; |
194 | |
195 | if (!sourceFrame.normals.empty()) |
196 | destFrame.normals.resize(vertexCount); |
197 | |
198 | if (!sourceFrame.tangents.empty()) |
199 | destFrame.tangents.resize(vertexCount); |
200 | |
201 | if (!sourceFrame.bitangents.empty()) |
202 | destFrame.bitangents.resize(vertexCount); |
203 | } |
204 | } |
205 | |
206 | Vector<Vector<int>> splitsPerVertex; |
207 | splitsPerVertex.resize(source.positions.size()); |
208 | |
209 | Vector<int>& indices = dest.indices; |
210 | int indexCount = (int)dest.indices.size(); |
211 | for (int i = 0; i < indexCount; i++) |
212 | { |
213 | int srcVertIdx = indices[i]; |
214 | int dstVertIdx = -1; |
215 | |
216 | // See if we already processed this vertex and if its attributes |
217 | // are close enough with the previous version |
218 | Vector<int>& splits = splitsPerVertex[srcVertIdx]; |
219 | for (auto& splitVertIdx : splits) |
220 | { |
221 | if (!needsSplitAttributes(source, i, dest, splitVertIdx)) |
222 | dstVertIdx = splitVertIdx; |
223 | } |
224 | |
225 | // We didn't find a close-enough match |
226 | if (dstVertIdx == -1) |
227 | { |
228 | // First time we visited this vertex, so just copy over attributes |
229 | if (splits.empty()) |
230 | { |
231 | dstVertIdx = srcVertIdx; |
232 | copyVertexAttributes(source, i, dest, dstVertIdx); |
233 | } |
234 | else // Split occurred, add a brand new vertex |
235 | { |
236 | dstVertIdx = (int)dest.positions.size(); |
237 | addVertex(source, i, srcVertIdx, dest); |
238 | } |
239 | |
240 | splits.push_back(dstVertIdx); |
241 | } |
242 | |
243 | indices[i] = dstVertIdx; |
244 | } |
245 | } |
246 | |
247 | void FBXUtility::flipWindingOrder(FBXImportMesh& input) |
248 | { |
249 | for (UINT32 i = 0; i < (UINT32)input.materials.size(); i += 3) |
250 | { |
251 | std::swap(input.materials[i + 1], input.materials[i + 2]); |
252 | } |
253 | |
254 | for (UINT32 i = 0; i < (UINT32)input.indices.size(); i += 3) |
255 | { |
256 | std::swap(input.indices[i + 1], input.indices[i + 2]); |
257 | } |
258 | } |
259 | |
260 | void FBXUtility::copyVertexAttributes(const FBXImportMesh& srcMesh, int srcIdx, FBXImportMesh& destMesh, int dstIdx) |
261 | { |
262 | if (!srcMesh.normals.empty()) |
263 | destMesh.normals[dstIdx] = srcMesh.normals[srcIdx]; |
264 | |
265 | if (!srcMesh.tangents.empty()) |
266 | destMesh.tangents[dstIdx] = srcMesh.tangents[srcIdx]; |
267 | |
268 | if (!srcMesh.bitangents.empty()) |
269 | destMesh.bitangents[dstIdx] = srcMesh.bitangents[srcIdx]; |
270 | |
271 | if (!srcMesh.colors.empty()) |
272 | destMesh.colors[dstIdx] = srcMesh.colors[srcIdx]; |
273 | |
274 | for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++) |
275 | { |
276 | if (!srcMesh.UV[i].empty()) |
277 | destMesh.UV[i][dstIdx] = srcMesh.UV[i][srcIdx]; |
278 | } |
279 | |
280 | UINT32 numBlendShapes = (UINT32)srcMesh.blendShapes.size(); |
281 | for (UINT32 i = 0; i < numBlendShapes; i++) |
282 | { |
283 | const FBXBlendShape& sourceShape = srcMesh.blendShapes[i]; |
284 | FBXBlendShape& destShape = destMesh.blendShapes[i]; |
285 | |
286 | UINT32 numFrames = (UINT32)sourceShape.frames.size(); |
287 | for (UINT32 j = 0; j < numFrames; j++) |
288 | { |
289 | const FBXBlendShapeFrame& sourceFrame = sourceShape.frames[j]; |
290 | FBXBlendShapeFrame& destFrame = destShape.frames[j]; |
291 | |
292 | if (!sourceFrame.normals.empty()) |
293 | destFrame.normals[dstIdx] = sourceFrame.normals[srcIdx]; |
294 | |
295 | if (!sourceFrame.tangents.empty()) |
296 | destFrame.tangents[dstIdx] = sourceFrame.tangents[srcIdx]; |
297 | |
298 | if (!sourceFrame.bitangents.empty()) |
299 | destFrame.bitangents[dstIdx] = sourceFrame.bitangents[srcIdx]; |
300 | } |
301 | } |
302 | } |
303 | |
304 | void FBXUtility::addVertex(const FBXImportMesh& srcMesh, int srcIdx, int srcVertex, FBXImportMesh& destMesh) |
305 | { |
306 | destMesh.positions.push_back(srcMesh.positions[srcVertex]); |
307 | |
308 | if (!srcMesh.boneInfluences.empty()) |
309 | destMesh.boneInfluences.push_back(srcMesh.boneInfluences[srcVertex]); |
310 | |
311 | if (!srcMesh.normals.empty()) |
312 | destMesh.normals.push_back(srcMesh.normals[srcIdx]); |
313 | |
314 | if (!srcMesh.tangents.empty()) |
315 | destMesh.tangents.push_back(srcMesh.tangents[srcIdx]); |
316 | |
317 | if (!srcMesh.bitangents.empty()) |
318 | destMesh.bitangents.push_back(srcMesh.bitangents[srcIdx]); |
319 | |
320 | if (!srcMesh.colors.empty()) |
321 | destMesh.colors.push_back(srcMesh.colors[srcIdx]); |
322 | |
323 | for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++) |
324 | { |
325 | if (!srcMesh.UV[i].empty()) |
326 | destMesh.UV[i].push_back(srcMesh.UV[i][srcIdx]); |
327 | } |
328 | |
329 | UINT32 numBlendShapes = (UINT32)srcMesh.blendShapes.size(); |
330 | for (UINT32 i = 0; i < numBlendShapes; i++) |
331 | { |
332 | const FBXBlendShape& sourceShape = srcMesh.blendShapes[i]; |
333 | FBXBlendShape& destShape = destMesh.blendShapes[i]; |
334 | |
335 | UINT32 numFrames = (UINT32)sourceShape.frames.size(); |
336 | for (UINT32 j = 0; j < numFrames; j++) |
337 | { |
338 | const FBXBlendShapeFrame& sourceFrame = sourceShape.frames[j]; |
339 | FBXBlendShapeFrame& destFrame = destShape.frames[j]; |
340 | |
341 | destFrame.positions.push_back(sourceFrame.positions[srcVertex]); |
342 | |
343 | if (!sourceFrame.normals.empty()) |
344 | destFrame.normals.push_back(sourceFrame.normals[srcIdx]); |
345 | |
346 | if (!sourceFrame.tangents.empty()) |
347 | destFrame.tangents.push_back(sourceFrame.tangents[srcIdx]); |
348 | |
349 | if (!sourceFrame.bitangents.empty()) |
350 | destFrame.bitangents.push_back(sourceFrame.bitangents[srcIdx]); |
351 | } |
352 | } |
353 | } |
354 | |
355 | bool FBXUtility::needsSplitAttributes(const FBXImportMesh& meshA, int idxA, const FBXImportMesh& meshB, int idxB) |
356 | { |
357 | static const float SplitAngleCosine = Math::cos(Degree(1.0f)); |
358 | static const float UVEpsilon = 0.001f; |
359 | |
360 | if (!meshA.colors.empty()) |
361 | { |
362 | if (meshA.colors[idxA] != meshB.colors[idxB]) |
363 | return true; |
364 | } |
365 | |
366 | if (!meshA.normals.empty()) |
367 | { |
368 | float angleCosine = meshA.normals[idxA].dot(meshB.normals[idxB]); |
369 | if (angleCosine < SplitAngleCosine) |
370 | return true; |
371 | } |
372 | |
373 | if (!meshA.tangents.empty()) |
374 | { |
375 | float angleCosine = meshA.tangents[idxA].dot(meshB.tangents[idxB]); |
376 | if (angleCosine < SplitAngleCosine) |
377 | return true; |
378 | } |
379 | |
380 | if (!meshA.bitangents.empty()) |
381 | { |
382 | float angleCosine = meshA.bitangents[idxA].dot(meshB.bitangents[idxB]); |
383 | if (angleCosine < SplitAngleCosine) |
384 | return true; |
385 | } |
386 | |
387 | for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++) |
388 | { |
389 | if (!meshA.UV[i].empty()) |
390 | { |
391 | if (!Math::approxEquals(meshA.UV[i][idxA], meshB.UV[i][idxB], UVEpsilon)) |
392 | return true; |
393 | } |
394 | } |
395 | |
396 | UINT32 numBlendShapes = (UINT32)meshA.blendShapes.size(); |
397 | for (UINT32 i = 0; i < numBlendShapes; i++) |
398 | { |
399 | const FBXBlendShape& shapeA = meshA.blendShapes[i]; |
400 | const FBXBlendShape& shapeB = meshB.blendShapes[i]; |
401 | |
402 | UINT32 numFrames = (UINT32)shapeA.frames.size(); |
403 | for (UINT32 j = 0; j < numFrames; j++) |
404 | { |
405 | const FBXBlendShapeFrame& frameA = shapeA.frames[j]; |
406 | const FBXBlendShapeFrame& frameB = shapeB.frames[j]; |
407 | |
408 | if (!frameA.normals.empty()) |
409 | { |
410 | float angleCosine = frameA.normals[idxA].dot(frameB.normals[idxB]); |
411 | if (angleCosine < SplitAngleCosine) |
412 | return true; |
413 | } |
414 | |
415 | if (!frameA.tangents.empty()) |
416 | { |
417 | float angleCosine = frameA.tangents[idxA].dot(frameB.tangents[idxB]); |
418 | if (angleCosine < SplitAngleCosine) |
419 | return true; |
420 | } |
421 | |
422 | if (!frameA.bitangents.empty()) |
423 | { |
424 | float angleCosine = frameA.bitangents[idxA].dot(frameB.bitangents[idxB]); |
425 | if (angleCosine < SplitAngleCosine) |
426 | return true; |
427 | } |
428 | } |
429 | } |
430 | |
431 | return false; |
432 | } |
433 | } |