GltfImporterMesh.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Importer/GltfImporter.h>
  6. #include <AnKi/Util/StringList.h>
  7. #include <AnKi/Collision/Plane.h>
  8. #include <AnKi/Collision/Functions.h>
  9. #include <AnKi/Collision/Sphere.h>
  10. #include <AnKi/Resource/MeshBinary.h>
  11. #include <AnKi/Shaders/Include/MeshTypes.h>
  12. #include <MeshOptimizer/meshoptimizer.h>
  13. namespace anki {
  14. static U cgltfComponentCount(cgltf_type type)
  15. {
  16. U out;
  17. switch(type)
  18. {
  19. case cgltf_type_scalar:
  20. out = 1;
  21. break;
  22. case cgltf_type_vec2:
  23. out = 2;
  24. break;
  25. case cgltf_type_vec3:
  26. out = 3;
  27. break;
  28. case cgltf_type_vec4:
  29. out = 4;
  30. break;
  31. default:
  32. ANKI_ASSERT(!"TODO");
  33. out = 0;
  34. }
  35. return out;
  36. }
  37. static U cgltfComponentSize(cgltf_component_type type)
  38. {
  39. U out;
  40. switch(type)
  41. {
  42. case cgltf_component_type_r_32f:
  43. out = sizeof(F32);
  44. break;
  45. case cgltf_component_type_r_16u:
  46. out = sizeof(U16);
  47. break;
  48. case cgltf_component_type_r_8u:
  49. out = sizeof(U8);
  50. break;
  51. default:
  52. ANKI_ASSERT(!"TODO");
  53. out = 0;
  54. }
  55. return out;
  56. }
  57. #if 0
  58. static U calcImplicitStride(const cgltf_attribute& attrib)
  59. {
  60. return cgltfComponentCount(attrib.data->type) * cgltfComponentSize(attrib.data->component_type);
  61. }
  62. #endif
  63. template<typename T>
  64. static Error checkAttribute(const cgltf_attribute& attrib)
  65. {
  66. if(cgltfComponentCount(attrib.data->type) != T::kComponentCount)
  67. {
  68. ANKI_IMPORTER_LOGE("Wrong component count for attribute: %s", attrib.name);
  69. return Error::kUserData;
  70. }
  71. if(cgltfComponentSize(attrib.data->component_type) != sizeof(typename T::Scalar))
  72. {
  73. ANKI_IMPORTER_LOGE("Incompatible type: %s", attrib.name);
  74. return Error::kUserData;
  75. }
  76. ANKI_ASSERT(attrib.data);
  77. const U count = U(attrib.data->count);
  78. if(count == 0)
  79. {
  80. ANKI_IMPORTER_LOGE("Zero vertex count");
  81. return Error::kUserData;
  82. }
  83. return Error::kNone;
  84. }
  85. class TempVertex
  86. {
  87. public:
  88. Vec3 m_position;
  89. F32 m_padding0;
  90. Vec4 m_boneWeights;
  91. Vec3 m_normal;
  92. F32 m_padding1;
  93. Vec2 m_uv;
  94. U16Vec4 m_boneIds;
  95. TempVertex()
  96. {
  97. zeroMemory(*this);
  98. }
  99. };
  100. static_assert(sizeof(TempVertex) == 4 * sizeof(Vec4), "Will be hashed");
  101. class ImporterMeshlet
  102. {
  103. public:
  104. U32 m_firstVertex = 0;
  105. U32 m_vertexCount = 0;
  106. U32 m_firstLocalIndex = 0;
  107. U32 m_localIndexCount = 0;
  108. Vec3 m_coneApex;
  109. Vec3 m_coneDir;
  110. F32 m_coneAngle = 0.0f;
  111. Sphere m_sphere;
  112. Aabb m_aabb;
  113. };
  114. class SubMesh
  115. {
  116. public:
  117. ImporterDynamicArray<TempVertex> m_verts;
  118. ImporterDynamicArray<U32> m_indices;
  119. ImporterDynamicArray<ImporterMeshlet> m_meshlets;
  120. ImporterDynamicArray<U8> m_localIndices;
  121. Vec3 m_aabbMin = Vec3(kMaxF32);
  122. Vec3 m_aabbMax = Vec3(kMinF32);
  123. Vec3 m_sphereCenter;
  124. F32 m_sphereRadius = 0.0f;
  125. };
  126. static void reindexSubmesh(SubMesh& submesh)
  127. {
  128. const U32 vertSize = sizeof(submesh.m_verts[0]);
  129. ImporterDynamicArray<U32> remap;
  130. remap.resize(submesh.m_verts.getSize(), 0);
  131. const U32 vertCount = U32(meshopt_generateVertexRemap(&remap[0], &submesh.m_indices[0], submesh.m_indices.getSize(), &submesh.m_verts[0],
  132. submesh.m_verts.getSize(), vertSize));
  133. ImporterDynamicArray<U32> newIdxArray;
  134. newIdxArray.resize(submesh.m_indices.getSize(), 0);
  135. ImporterDynamicArray<TempVertex> newVertArray;
  136. newVertArray.resize(vertCount);
  137. meshopt_remapIndexBuffer(&newIdxArray[0], &submesh.m_indices[0], submesh.m_indices.getSize(), &remap[0]);
  138. meshopt_remapVertexBuffer(&newVertArray[0], &submesh.m_verts[0], submesh.m_verts.getSize(), vertSize, &remap[0]);
  139. submesh.m_indices = std::move(newIdxArray);
  140. submesh.m_verts = std::move(newVertArray);
  141. }
  142. /// Optimize a submesh using meshoptimizer.
  143. static void optimizeSubmesh(SubMesh& submesh)
  144. {
  145. const PtrSize vertSize = sizeof(submesh.m_verts[0]);
  146. // Vert cache
  147. {
  148. ImporterDynamicArray<U32> newIdxArray;
  149. newIdxArray.resize(submesh.m_indices.getSize(), 0);
  150. meshopt_optimizeVertexCache(&newIdxArray[0], &submesh.m_indices[0], submesh.m_indices.getSize(), submesh.m_verts.getSize());
  151. submesh.m_indices = std::move(newIdxArray);
  152. }
  153. // Overdraw
  154. {
  155. ImporterDynamicArray<U32> newIdxArray;
  156. newIdxArray.resize(submesh.m_indices.getSize(), 0);
  157. meshopt_optimizeOverdraw(&newIdxArray[0], &submesh.m_indices[0], submesh.m_indices.getSize(), &submesh.m_verts[0].m_position.x,
  158. submesh.m_verts.getSize(), vertSize, 1.05f);
  159. submesh.m_indices = std::move(newIdxArray);
  160. }
  161. // Vert fetch
  162. {
  163. ImporterDynamicArray<TempVertex> newVertArray;
  164. newVertArray.resize(submesh.m_verts.getSize());
  165. const U32 newVertCount =
  166. U32(meshopt_optimizeVertexFetch(&newVertArray[0],
  167. &submesh.m_indices[0], // Inplace
  168. submesh.m_indices.getSize(), &submesh.m_verts[0], submesh.m_verts.getSize(), vertSize));
  169. if(newVertCount != submesh.m_verts.getSize())
  170. {
  171. newVertArray.resize(newVertCount);
  172. }
  173. ANKI_ASSERT(newVertArray.getSize() == newVertCount);
  174. submesh.m_verts = std::move(newVertArray);
  175. }
  176. }
  177. /// Decimate a submesh using meshoptimizer.
  178. static void decimateSubmesh(F32 factor, SubMesh& submesh)
  179. {
  180. ANKI_ASSERT(factor > 0.0f && factor < 1.0f);
  181. const PtrSize targetIndexCount = PtrSize(F32(submesh.m_indices.getSize() / 3) * factor) * 3;
  182. if(targetIndexCount == 0)
  183. {
  184. return;
  185. }
  186. // Decimate
  187. ImporterDynamicArray<U32> newIndices;
  188. newIndices.resize(submesh.m_indices.getSize());
  189. newIndices.resize(U32(meshopt_simplify(&newIndices[0], &submesh.m_indices[0], submesh.m_indices.getSize(), &submesh.m_verts[0].m_position.x,
  190. submesh.m_verts.getSize(), sizeof(TempVertex), targetIndexCount, 1e-2f)));
  191. // Re-pack
  192. ImporterDynamicArray<U32> reindexedIndices;
  193. ImporterDynamicArray<TempVertex> newVerts;
  194. ImporterHashMap<U32, U32> vertexStored;
  195. for(U32 idx = 0; idx < newIndices.getSize(); ++idx)
  196. {
  197. U32 newIdx;
  198. auto it = vertexStored.find(newIndices[idx]);
  199. if(it == vertexStored.getEnd())
  200. {
  201. // Store the vertex
  202. newVerts.emplaceBack(submesh.m_verts[newIndices[idx]]);
  203. newIdx = newVerts.getSize() - 1;
  204. vertexStored.emplace(newIndices[idx], newIdx);
  205. }
  206. else
  207. {
  208. // Already stored
  209. newIdx = *it;
  210. }
  211. // Store the new index
  212. reindexedIndices.emplaceBack(newIdx);
  213. }
  214. // Move back
  215. submesh.m_indices = std::move(reindexedIndices);
  216. submesh.m_verts = std::move(newVerts);
  217. }
  218. /// If normal A and normal B have the same position then try to merge them. Do that before optimizations.
  219. static void fixNormals(const F32 normalsMergeAngle, SubMesh& submesh)
  220. {
  221. for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
  222. {
  223. const Vec3& pos = submesh.m_verts[v].m_position;
  224. Vec3& normal = submesh.m_verts[v].m_normal;
  225. for(U32 prevV = 0; prevV < v; ++prevV)
  226. {
  227. const Vec3& otherPos = submesh.m_verts[prevV].m_position;
  228. // Check the positions dist
  229. const F32 posDist = (otherPos - pos).lengthSquared();
  230. if(posDist > kEpsilonf * kEpsilonf)
  231. {
  232. continue;
  233. }
  234. // Check angle of the normals
  235. Vec3& otherNormal = submesh.m_verts[prevV].m_normal;
  236. const F32 ang = acos(clamp(otherNormal.dot(normal), -1.0f, 1.0f));
  237. if(ang > normalsMergeAngle)
  238. {
  239. continue;
  240. }
  241. // Merge normals
  242. const Vec3 newNormal = (otherNormal + normal).normalize();
  243. normal = newNormal;
  244. otherNormal = newNormal;
  245. }
  246. }
  247. }
  248. static Bool isConvex(const ImporterList<SubMesh>& submeshes)
  249. {
  250. Bool convex = true;
  251. for(const SubMesh& submesh : submeshes)
  252. {
  253. for(U32 i = 0; i < submesh.m_indices.getSize(); i += 3)
  254. {
  255. const U32 i0 = submesh.m_indices[i + 0];
  256. const U32 i1 = submesh.m_indices[i + 1];
  257. const U32 i2 = submesh.m_indices[i + 2];
  258. const Vec3& v0 = submesh.m_verts[i0].m_position;
  259. const Vec3& v1 = submesh.m_verts[i1].m_position;
  260. const Vec3& v2 = submesh.m_verts[i2].m_position;
  261. if(computeTriangleArea(v0, v1, v2) <= kEpsilonf)
  262. {
  263. continue;
  264. }
  265. // Check that all positions are behind the plane
  266. const Plane plane(v0.xyz0, v1.xyz0, v2.xyz0);
  267. for(const SubMesh& submeshB : submeshes)
  268. {
  269. for(const TempVertex& vertB : submeshB.m_verts)
  270. {
  271. const F32 test = testPlane(plane, vertB.m_position.xyz0);
  272. if(test > kEpsilonf)
  273. {
  274. convex = false;
  275. break;
  276. }
  277. }
  278. if(!convex)
  279. {
  280. break;
  281. }
  282. }
  283. if(!convex)
  284. {
  285. break;
  286. }
  287. }
  288. }
  289. return convex;
  290. }
  291. static void generateMeshlets(SubMesh& submesh)
  292. {
  293. // Allocate the arrays
  294. const U32 maxMeshlets = U32(meshopt_buildMeshletsBound(submesh.m_indices.getSize(), kMaxVerticesPerMeshlet, kMaxPrimitivesPerMeshlet));
  295. ImporterDynamicArray<U32> indicesToVertexBuffer;
  296. indicesToVertexBuffer.resize(maxMeshlets * kMaxVerticesPerMeshlet);
  297. ImporterDynamicArray<U8> localIndices;
  298. localIndices.resize(maxMeshlets * kMaxPrimitivesPerMeshlet * 3);
  299. ImporterDynamicArray<meshopt_Meshlet> meshlets;
  300. meshlets.resize(maxMeshlets);
  301. // Meshletize
  302. constexpr F32 coneWeight = 0.0f;
  303. const U32 meshletCount =
  304. U32(meshopt_buildMeshlets(meshlets.getBegin(), indicesToVertexBuffer.getBegin(), localIndices.getBegin(), submesh.m_indices.getBegin(),
  305. submesh.m_indices.getSize(), &submesh.m_verts[0].m_position.x, submesh.m_verts.getSize(), sizeof(TempVertex),
  306. kMaxVerticesPerMeshlet, kMaxPrimitivesPerMeshlet, coneWeight));
  307. // Trim the arrays
  308. const meshopt_Meshlet& last = meshlets[meshletCount - 1u];
  309. indicesToVertexBuffer.resize(last.vertex_offset + last.vertex_count);
  310. localIndices.resize(last.triangle_offset + ((last.triangle_count * 3u + 3u) & ~3u));
  311. meshlets.resize(meshletCount);
  312. // Create the new vertex and global index buffer
  313. submesh.m_meshlets.destroy();
  314. submesh.m_meshlets.resize(meshletCount);
  315. submesh.m_localIndices.destroy();
  316. ImporterDynamicArray<U32> newIndexBuffer;
  317. ImporterDynamicArray<TempVertex> newVertexBuffer;
  318. for(U32 meshletIdx = 0; meshletIdx < meshletCount; ++meshletIdx)
  319. {
  320. const meshopt_Meshlet& inMeshlet = meshlets[meshletIdx];
  321. ImporterMeshlet& outMeshlet = submesh.m_meshlets[meshletIdx];
  322. outMeshlet.m_firstLocalIndex = submesh.m_localIndices.getSize();
  323. outMeshlet.m_localIndexCount = inMeshlet.triangle_count * 3;
  324. outMeshlet.m_firstVertex = newVertexBuffer.getSize();
  325. outMeshlet.m_vertexCount = inMeshlet.vertex_count;
  326. ImporterHashMap<U8, U32> localIndexToNewGlobalIndex;
  327. for(U32 tri = 0; tri < inMeshlet.triangle_count; ++tri)
  328. {
  329. const U8Vec3 localIdx3(localIndices[inMeshlet.triangle_offset + tri * 3], localIndices[inMeshlet.triangle_offset + tri * 3 + 1],
  330. localIndices[inMeshlet.triangle_offset + tri * 3 + 2]);
  331. for(U32 j = 0; j < 3; j++)
  332. {
  333. const U8 localIdx = localIdx3[j];
  334. // Search if we processed the same index before
  335. auto it = localIndexToNewGlobalIndex.find(localIdx);
  336. const Bool newVertex = (it == localIndexToNewGlobalIndex.getEnd());
  337. // Add the vertex, global index
  338. if(newVertex)
  339. {
  340. const U32 newGlobalIdx = newVertexBuffer.getSize();
  341. newIndexBuffer.emplaceBack(newGlobalIdx);
  342. const U32 globalIdx = indicesToVertexBuffer[inMeshlet.vertex_offset + localIdx];
  343. const TempVertex vert = submesh.m_verts[globalIdx];
  344. newVertexBuffer.emplaceBack(vert);
  345. localIndexToNewGlobalIndex.emplace(localIdx, newGlobalIdx);
  346. }
  347. else
  348. {
  349. const U32 newGlobalIdx = *it;
  350. newIndexBuffer.emplaceBack(newGlobalIdx);
  351. }
  352. // Append the local index
  353. submesh.m_localIndices.emplaceBack(localIdx);
  354. }
  355. }
  356. ANKI_ASSERT(localIndexToNewGlobalIndex.getSize() == outMeshlet.m_vertexCount);
  357. // Compute bounds
  358. const meshopt_Bounds bounds =
  359. meshopt_computeMeshletBounds(&indicesToVertexBuffer[inMeshlet.vertex_offset], &localIndices[inMeshlet.triangle_offset],
  360. inMeshlet.triangle_count, &submesh.m_verts[0].m_position.x, submesh.m_verts.getSize(), sizeof(TempVertex));
  361. outMeshlet.m_coneApex = Vec3(&bounds.cone_apex[0]);
  362. outMeshlet.m_coneDir = Vec3(&bounds.cone_axis[0]);
  363. outMeshlet.m_coneAngle = acos(bounds.cone_cutoff) * 2.0f;
  364. outMeshlet.m_sphere =
  365. computeBoundingSphere(&newVertexBuffer[outMeshlet.m_firstVertex].m_position, outMeshlet.m_vertexCount, sizeof(TempVertex));
  366. if(bounds.radius < outMeshlet.m_sphere.getRadius() && bounds.radius > 0.0f)
  367. {
  368. // meshopt computed smaller sphere, use that one
  369. outMeshlet.m_sphere.setCenter(Vec3(&bounds.center[0]));
  370. outMeshlet.m_sphere.setRadius(bounds.radius);
  371. }
  372. outMeshlet.m_aabb = computeBoundingAabb(&newVertexBuffer[outMeshlet.m_firstVertex].m_position, outMeshlet.m_vertexCount, sizeof(TempVertex));
  373. }
  374. const F64 avgPrimCountPerMeshlet = F64(newIndexBuffer.getSize() / 3) / F64(meshletCount);
  375. const F64 avgVertCountPerMeshlet = F64(newVertexBuffer.getSize()) / F64(meshletCount);
  376. ANKI_IMPORTER_LOGV("Meshletization stats: %f%% more vertices, %u meshlets, primitive_count/meshlet %f, vert_count/meshlet %f",
  377. (F32(newVertexBuffer.getSize()) - F32(submesh.m_verts.getSize())) / F32(submesh.m_verts.getSize()) * 100.0f, meshletCount,
  378. avgPrimCountPerMeshlet, avgVertCountPerMeshlet);
  379. submesh.m_indices = std::move(newIndexBuffer);
  380. submesh.m_verts = std::move(newVertexBuffer);
  381. }
  382. static void writeVertexAttribAndBufferInfoToHeader(VertexStreamId stream, MeshBinaryHeader& header, const Vec4& scale = Vec4(1.0f),
  383. const Vec4& translation = Vec4(0.0f))
  384. {
  385. MeshBinaryVertexAttribute& attrib = header.m_vertexAttributes[stream];
  386. attrib.m_bufferIndex = U32(stream);
  387. attrib.m_format = kMeshRelatedVertexStreamFormats[stream];
  388. attrib.m_relativeOffset = 0;
  389. attrib.m_scale = {scale[0], scale[1], scale[2], scale[3]};
  390. attrib.m_translation = {translation[0], translation[1], translation[2], translation[3]};
  391. MeshBinaryVertexBuffer& buff = header.m_vertexBuffers[stream];
  392. buff.m_vertexStride = getFormatInfo(attrib.m_format).m_texelSize;
  393. }
  394. U32 GltfImporter::getMeshTotalVertexCount(const cgltf_mesh& mesh)
  395. {
  396. U32 totalVertexCount = 0;
  397. for(const cgltf_primitive* primitive = mesh.primitives; primitive < mesh.primitives + mesh.primitives_count; ++primitive)
  398. {
  399. totalVertexCount += U32(primitive->attributes[0].data->count);
  400. }
  401. return totalVertexCount;
  402. }
  403. Error GltfImporter::writeMesh(const cgltf_mesh& mesh) const
  404. {
  405. const Error err = writeMeshInternal(mesh);
  406. if(err)
  407. {
  408. ANKI_IMPORTER_LOGE("Failed to write mesh: %s", mesh.name);
  409. }
  410. return err;
  411. }
  412. Error GltfImporter::writeMeshInternal(const cgltf_mesh& mesh) const
  413. {
  414. const ImporterString meshName = computeMeshResourceFilename(mesh);
  415. ImporterString fname;
  416. fname.sprintf("%s%s", m_outDir.cstr(), meshName.cstr());
  417. ANKI_IMPORTER_LOGV("Importing mesh (%s): %s", (m_optimizeMeshes) ? "optimize" : "WON'T optimize", fname.cstr());
  418. Array<ImporterList<SubMesh>, kMaxLodCount> submeshes;
  419. Vec3 aabbMin(kMaxF32);
  420. Vec3 aabbMax(kMinF32);
  421. Bool hasBoneWeights = false;
  422. ImporterDynamicArray<Vec3> allPositions; // Used to calculate the overall bounding sphere
  423. // Iterate primitives. Every primitive is a submesh
  424. for(const cgltf_primitive* primitive = mesh.primitives; primitive < mesh.primitives + mesh.primitives_count; ++primitive)
  425. {
  426. if(primitive->type != cgltf_primitive_type_triangles)
  427. {
  428. ANKI_IMPORTER_LOGE("Expecting triangles got %d", primitive->type);
  429. return Error::kUserData;
  430. }
  431. SubMesh& submesh = *submeshes[0].emplaceBack();
  432. // All attributes should have the same vertex count
  433. U minVertCount = kMaxU;
  434. U maxVertCount = kMinU;
  435. for(const cgltf_attribute* attrib = primitive->attributes; attrib < primitive->attributes + primitive->attributes_count; ++attrib)
  436. {
  437. minVertCount = min(minVertCount, U(attrib->data->count));
  438. maxVertCount = max(maxVertCount, U(attrib->data->count));
  439. }
  440. if(maxVertCount == 0 || minVertCount != maxVertCount)
  441. {
  442. ANKI_IMPORTER_LOGE("Wrong number of vertices");
  443. return Error::kUserData;
  444. }
  445. U32 vertCount = U32(primitive->attributes[0].data->count);
  446. submesh.m_verts.resize(vertCount);
  447. //
  448. // Gather positions + normals + UVs + bone stuff
  449. //
  450. for(const cgltf_attribute* attrib = primitive->attributes; attrib < primitive->attributes + primitive->attributes_count; ++attrib)
  451. {
  452. if(attrib->type == cgltf_attribute_type_position)
  453. {
  454. U32 count = 0;
  455. ANKI_CHECK(checkAttribute<Vec3>(*attrib));
  456. visitAccessor<Vec3>(*attrib->data, [&](const Vec3& pos) {
  457. submesh.m_aabbMin = submesh.m_aabbMin.min(pos);
  458. submesh.m_aabbMax = submesh.m_aabbMax.max(pos);
  459. submesh.m_verts[count++].m_position = pos;
  460. allPositions.emplaceBack(pos);
  461. });
  462. }
  463. else if(attrib->type == cgltf_attribute_type_normal)
  464. {
  465. U32 count = 0;
  466. ANKI_CHECK(checkAttribute<Vec3>(*attrib));
  467. visitAccessor<Vec3>(*attrib->data, [&](const Vec3& normal) {
  468. submesh.m_verts[count++].m_normal = normal;
  469. });
  470. }
  471. else if(attrib->type == cgltf_attribute_type_texcoord && CString(attrib->name) == "TEXCOORD_0")
  472. {
  473. U32 count = 0;
  474. ANKI_CHECK(checkAttribute<Vec2>(*attrib));
  475. visitAccessor<Vec2>(*attrib->data, [&](const Vec2& uv) {
  476. submesh.m_verts[count++].m_uv = uv;
  477. });
  478. }
  479. else if(attrib->type == cgltf_attribute_type_joints)
  480. {
  481. U32 count = 0;
  482. if(cgltfComponentSize(attrib->data->component_type) == 2)
  483. {
  484. ANKI_CHECK(checkAttribute<U16Vec4>(*attrib));
  485. visitAccessor<U16Vec4>(*attrib->data, [&](const U16Vec4& x) {
  486. submesh.m_verts[count++].m_boneIds = x;
  487. });
  488. }
  489. else
  490. {
  491. ANKI_CHECK(checkAttribute<U8Vec4>(*attrib));
  492. visitAccessor<U8Vec4>(*attrib->data, [&](const U8Vec4& x) {
  493. submesh.m_verts[count++].m_boneIds = U16Vec4(x);
  494. });
  495. }
  496. hasBoneWeights = true;
  497. }
  498. else if(attrib->type == cgltf_attribute_type_weights)
  499. {
  500. U32 count = 0;
  501. ANKI_CHECK(checkAttribute<Vec4>(*attrib));
  502. visitAccessor<Vec4>(*attrib->data, [&](const Vec4& bw) {
  503. submesh.m_verts[count++].m_boneWeights = bw;
  504. });
  505. }
  506. else
  507. {
  508. ANKI_IMPORTER_LOGV("Ignoring attribute: %s", attrib->name);
  509. }
  510. }
  511. aabbMin = aabbMin.min(submesh.m_aabbMin);
  512. submesh.m_aabbMax += kEpsilonf * 10.0f; // Bump aabbMax a bit
  513. aabbMax = aabbMax.max(submesh.m_aabbMax);
  514. const Sphere s = computeBoundingSphere(&submesh.m_verts[0].m_position, submesh.m_verts.getSize(), sizeof(submesh.m_verts[0]));
  515. submesh.m_sphereCenter = s.getCenter().xyz;
  516. submesh.m_sphereRadius = max(kEpsilonf * 10.0f, s.getRadius());
  517. // Fix normals
  518. fixNormals(m_normalsMergeAngle, submesh);
  519. //
  520. // Load indices
  521. //
  522. {
  523. ANKI_ASSERT(primitive->indices);
  524. if(primitive->indices->count == 0 || (primitive->indices->count % 3) != 0)
  525. {
  526. ANKI_IMPORTER_LOGE("Incorect index count: %lu", primitive->indices->count);
  527. return Error::kUserData;
  528. }
  529. submesh.m_indices.resize(U32(primitive->indices->count));
  530. const U8* base = static_cast<const U8*>(primitive->indices->buffer_view->buffer->data) + primitive->indices->offset
  531. + primitive->indices->buffer_view->offset;
  532. for(U32 i = 0; i < primitive->indices->count; ++i)
  533. {
  534. U32 idx;
  535. if(primitive->indices->component_type == cgltf_component_type_r_32u)
  536. {
  537. idx = *reinterpret_cast<const U32*>(base + sizeof(U32) * i);
  538. }
  539. else if(primitive->indices->component_type == cgltf_component_type_r_16u)
  540. {
  541. idx = *reinterpret_cast<const U16*>(base + sizeof(U16) * i);
  542. }
  543. else
  544. {
  545. ANKI_ASSERT(0);
  546. idx = 0;
  547. }
  548. submesh.m_indices[i] = idx;
  549. }
  550. }
  551. // Re-index meshes now
  552. if(m_optimizeMeshes)
  553. {
  554. reindexSubmesh(submesh);
  555. vertCount = submesh.m_verts.getSize();
  556. }
  557. // Optimize
  558. if(m_optimizeMeshes)
  559. {
  560. optimizeSubmesh(submesh);
  561. }
  562. // Finalize
  563. if(submesh.m_indices.getSize() == 0 || submesh.m_verts.getSize() == 0)
  564. {
  565. ANKI_UTIL_LOGE("Mesh degenerate: %s", meshName.cstr());
  566. return Error::kUserData;
  567. }
  568. } // for all GLTF primitives
  569. // Generate submeshes for the other LODs
  570. ANKI_ASSERT(m_lodCount <= kMaxLodCount && m_lodCount > 0);
  571. U32 maxLod = 0;
  572. for(U32 lod = 1; lod < m_lodCount; ++lod)
  573. {
  574. if(skipMeshLod(mesh, lod))
  575. {
  576. break;
  577. }
  578. for(const SubMesh& lod0Submesh : submeshes[0])
  579. {
  580. SubMesh& newSubmesh = *submeshes[lod].emplaceBack();
  581. newSubmesh = lod0Submesh; // Copy LOD0 data to new submesh
  582. decimateSubmesh(computeLodFactor(lod), newSubmesh);
  583. }
  584. maxLod = lod;
  585. }
  586. ANKI_IMPORTER_LOGV("Mesh lod count: %s %u", fname.cstr(), maxLod + 1);
  587. // Meshletize
  588. for(U32 lod = 0; lod < m_lodCount; ++lod)
  589. {
  590. for(SubMesh& submesh : submeshes[lod])
  591. {
  592. generateMeshlets(submesh);
  593. }
  594. }
  595. // Start writing the file
  596. File file;
  597. ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::kWrite | FileOpenFlag::kBinary));
  598. // Populate the header
  599. MeshBinaryHeader header;
  600. memset(&header, 0, sizeof(header));
  601. memcpy(&header.m_magic[0], kMeshMagic, 8);
  602. header.m_flags = (isConvex(submeshes[0])) ? MeshBinaryFlag::kConvex : MeshBinaryFlag::kNone;
  603. header.m_indexType = IndexType::kU16;
  604. header.m_meshletPrimitiveFormat = Format::kR8G8B8A8_Uint;
  605. header.m_subMeshCount = U32(submeshes[0].getSize());
  606. header.m_lodCount = maxLod + 1;
  607. header.m_maxPrimitivesPerMeshlet = kMaxPrimitivesPerMeshlet;
  608. header.m_maxVerticesPerMeshlet = kMaxVerticesPerMeshlet;
  609. header.m_boundingVolume.m_aabbMin = aabbMin;
  610. header.m_boundingVolume.m_aabbMax = aabbMax;
  611. const Sphere s = computeBoundingSphere(&allPositions[0], allPositions.getSize(), sizeof(allPositions[0]));
  612. header.m_boundingVolume.m_sphereCenter = s.getCenter().xyz;
  613. header.m_boundingVolume.m_sphereRadius = s.getRadius();
  614. // Compute the pos scale and transform. The scale is uniform because it's applied to the model matrix of the object
  615. F32 posScale = kMinF32;
  616. for(U c = 0; c < 3; c++)
  617. {
  618. posScale = max(posScale, aabbMax[c] - aabbMin[c]);
  619. }
  620. posScale = (posScale < 1.0f) ? 1.0f : (1.0f / posScale);
  621. const Vec3 posTranslation = -aabbMin;
  622. writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kPosition, header, Vec4(1.0f / posScale), (-posTranslation).xyz1);
  623. writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kNormal, header);
  624. writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kUv, header);
  625. if(hasBoneWeights)
  626. {
  627. writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kBoneIds, header);
  628. writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kBoneWeights, header);
  629. }
  630. // Write sub meshes
  631. ImporterDynamicArray<MeshBinarySubMesh> outSubmeshes;
  632. outSubmeshes.resize(U32(submeshes[0].getSize()));
  633. for(U32 submeshIdx = 0; submeshIdx < outSubmeshes.getSize(); ++submeshIdx)
  634. {
  635. MeshBinarySubMesh& out = outSubmeshes[submeshIdx];
  636. memset(&out, 0, sizeof(out));
  637. for(U32 lod = 0; lod <= maxLod; ++lod)
  638. {
  639. const SubMesh& inSubmesh = *(submeshes[lod].getBegin() + submeshIdx);
  640. if(lod == 0)
  641. {
  642. out.m_boundingVolume.m_aabbMin = inSubmesh.m_aabbMin;
  643. out.m_boundingVolume.m_aabbMax = inSubmesh.m_aabbMax;
  644. out.m_boundingVolume.m_sphereCenter = inSubmesh.m_sphereCenter;
  645. out.m_boundingVolume.m_sphereRadius = inSubmesh.m_sphereRadius;
  646. }
  647. out.m_lods[lod].m_firstIndex = header.m_indexCounts[lod];
  648. out.m_lods[lod].m_indexCount = inSubmesh.m_indices.getSize();
  649. out.m_lods[lod].m_firstMeshlet = header.m_meshletCounts[lod];
  650. out.m_lods[lod].m_meshletCount = inSubmesh.m_meshlets.getSize();
  651. header.m_indexCounts[lod] += inSubmesh.m_indices.getSize();
  652. header.m_vertexCounts[lod] += inSubmesh.m_verts.getSize();
  653. header.m_meshletCounts[lod] += inSubmesh.m_meshlets.getSize();
  654. header.m_meshletPrimitiveCounts[lod] += inSubmesh.m_localIndices.getSize() / 3u;
  655. }
  656. }
  657. ANKI_CHECK(file.write(&header, sizeof(header)));
  658. ANKI_CHECK(file.write(&outSubmeshes[0], outSubmeshes.getSizeInBytes()));
  659. // Write LODs
  660. for(I32 lod = I32(maxLod); lod >= 0; --lod)
  661. {
  662. // Write index buffer
  663. U32 vertCount = 0;
  664. for(const SubMesh& submesh : submeshes[lod])
  665. {
  666. ImporterDynamicArray<U16> indices;
  667. indices.resize(submesh.m_indices.getSize());
  668. for(U32 i = 0; i < indices.getSize(); ++i)
  669. {
  670. const U32 idx = submesh.m_indices[i] + vertCount;
  671. if(idx > kMaxU16)
  672. {
  673. ANKI_IMPORTER_LOGE("Only supports 16bit indices for now (%u): %s", idx, fname.cstr());
  674. return Error::kUserData;
  675. }
  676. indices[i] = U16(idx);
  677. }
  678. ANKI_CHECK(file.write(&indices[0], indices.getSizeInBytes()));
  679. vertCount += submesh.m_verts.getSize();
  680. }
  681. // Write positions
  682. for(const SubMesh& submesh : submeshes[lod])
  683. {
  684. ImporterDynamicArray<U16Vec4> positions;
  685. positions.resize(submesh.m_verts.getSize());
  686. for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
  687. {
  688. Vec3 localPos = (submesh.m_verts[v].m_position + posTranslation) * posScale;
  689. localPos = localPos.clamp(0.0f, 1.0f);
  690. localPos *= F32(kMaxU16);
  691. localPos = localPos.round();
  692. positions[v] = U16Vec4(localPos.xyz0);
  693. }
  694. ANKI_CHECK(file.write(&positions[0], positions.getSizeInBytes()));
  695. }
  696. // Write normals
  697. for(const SubMesh& submesh : submeshes[lod])
  698. {
  699. ImporterDynamicArray<U32> normals;
  700. normals.resize(submesh.m_verts.getSize());
  701. for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
  702. {
  703. normals[v] = submesh.m_verts[v].m_normal.xyz0.packSnorm4x8();
  704. }
  705. ANKI_CHECK(file.write(&normals[0], normals.getSizeInBytes()));
  706. }
  707. // Write UV
  708. for(const SubMesh& submesh : submeshes[lod])
  709. {
  710. ImporterDynamicArray<Vec2> uvs;
  711. uvs.resize(submesh.m_verts.getSize());
  712. for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
  713. {
  714. uvs[v] = submesh.m_verts[v].m_uv;
  715. }
  716. ANKI_CHECK(file.write(&uvs[0], uvs.getSizeInBytes()));
  717. }
  718. if(hasBoneWeights)
  719. {
  720. // Bone IDs
  721. for(const SubMesh& submesh : submeshes[lod])
  722. {
  723. ImporterDynamicArray<U8Vec4> boneids;
  724. boneids.resize(submesh.m_verts.getSize());
  725. for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
  726. {
  727. boneids[v] = U8Vec4(submesh.m_verts[v].m_boneIds);
  728. }
  729. ANKI_CHECK(file.write(&boneids[0], boneids.getSizeInBytes()));
  730. }
  731. // Bone weights
  732. for(const SubMesh& submesh : submeshes[lod])
  733. {
  734. ImporterDynamicArray<U32> boneWeights;
  735. boneWeights.resize(submesh.m_verts.getSize());
  736. for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
  737. {
  738. boneWeights[v] = submesh.m_verts[v].m_boneWeights.packSnorm4x8();
  739. }
  740. ANKI_CHECK(file.write(&boneWeights[0], boneWeights.getSizeInBytes()));
  741. }
  742. }
  743. // Write meshlets
  744. U32 primitiveCount = 0;
  745. U32 vertCount2 = 0;
  746. for(const SubMesh& submesh : submeshes[lod])
  747. {
  748. ImporterDynamicArray<MeshBinaryMeshlet> meshlets;
  749. meshlets.resize(submesh.m_meshlets.getSize());
  750. for(U32 v = 0; v < submesh.m_meshlets.getSize(); ++v)
  751. {
  752. MeshBinaryMeshlet& out = meshlets[v];
  753. const ImporterMeshlet& in = submesh.m_meshlets[v];
  754. out.m_firstPrimitive = primitiveCount;
  755. out.m_primitiveCount = in.m_localIndexCount / 3;
  756. primitiveCount += out.m_primitiveCount;
  757. out.m_firstVertex = vertCount2;
  758. out.m_vertexCount = in.m_vertexCount;
  759. vertCount2 += in.m_vertexCount;
  760. out.m_boundingVolume.m_sphereCenter = in.m_sphere.getCenter().xyz;
  761. out.m_boundingVolume.m_sphereRadius = in.m_sphere.getRadius();
  762. out.m_boundingVolume.m_aabbMin = in.m_aabb.getMin().xyz;
  763. out.m_boundingVolume.m_aabbMax = in.m_aabb.getMax().xyz;
  764. out.m_coneApex = in.m_coneApex;
  765. out.m_coneDirection = in.m_coneDir;
  766. out.m_coneAngle = in.m_coneAngle;
  767. }
  768. ANKI_CHECK(file.write(&meshlets[0], meshlets.getSizeInBytes()));
  769. }
  770. ANKI_ASSERT(vertCount2 == vertCount);
  771. ANKI_ASSERT(primitiveCount == header.m_meshletPrimitiveCounts[lod]);
  772. // Write local indices
  773. for(const SubMesh& submesh : submeshes[lod])
  774. {
  775. ImporterDynamicArray<U8> localIndices;
  776. localIndices.resize(submesh.m_localIndices.getSize() / 3 * sizeof(U8Vec4));
  777. U32 count = 0;
  778. for(U32 v = 0; v < submesh.m_localIndices.getSize(); v += 3)
  779. {
  780. localIndices[count++] = submesh.m_localIndices[v];
  781. localIndices[count++] = submesh.m_localIndices[v + 1];
  782. localIndices[count++] = submesh.m_localIndices[v + 2];
  783. localIndices[count++] = 0;
  784. }
  785. ANKI_CHECK(file.write(&localIndices[0], localIndices.getSizeInBytes()));
  786. }
  787. }
  788. return Error::kNone;
  789. }
  790. } // end namespace anki