MeshResource.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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/Resource/MeshResource.h>
  6. #include <AnKi/Resource/ResourceManager.h>
  7. #include <AnKi/Resource/MeshBinaryLoader.h>
  8. #include <AnKi/Resource/AsyncLoader.h>
  9. #include <AnKi/Resource/AccelerationStructureScratchAllocator.h>
  10. #include <AnKi/Util/Functions.h>
  11. #include <AnKi/Util/Filesystem.h>
  12. #include <AnKi/Core/App.h>
  13. #include <AnKi/Physics/PhysicsWorld.h>
  14. namespace anki {
  15. class MeshResource::LoadContext
  16. {
  17. public:
  18. MeshResourcePtr m_mesh;
  19. MeshBinaryLoader m_loader;
  20. LoadContext(MeshResource* mesh)
  21. : m_mesh(mesh)
  22. , m_loader(&ResourceMemoryPool::getSingleton())
  23. {
  24. }
  25. };
  26. /// Mesh upload async task.
  27. class MeshResource::LoadTask : public AsyncLoaderTask
  28. {
  29. public:
  30. MeshResource::LoadContext m_ctx;
  31. LoadTask(MeshResource* mesh)
  32. : m_ctx(mesh)
  33. {
  34. }
  35. Error operator()([[maybe_unused]] AsyncLoaderTaskContext& ctx) final
  36. {
  37. return m_ctx.m_mesh->loadAsync(m_ctx.m_loader);
  38. }
  39. static BaseMemoryPool& getMemoryPool()
  40. {
  41. return ResourceMemoryPool::getSingleton();
  42. }
  43. };
  44. MeshResource::~MeshResource()
  45. {
  46. for(Lod& lod : m_lods)
  47. {
  48. UnifiedGeometryBuffer::getSingleton().deferredFree(lod.m_indexBufferAllocationToken);
  49. for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
  50. {
  51. UnifiedGeometryBuffer::getSingleton().deferredFree(lod.m_vertexBuffersAllocationToken[stream]);
  52. }
  53. }
  54. }
  55. Error MeshResource::load(const ResourceFilename& filename, Bool async)
  56. {
  57. UniquePtr<LoadTask> task;
  58. LoadContext* ctx;
  59. LoadContext localCtx(this);
  60. String basename;
  61. getFilepathFilename(filename, basename);
  62. const Bool rayTracingEnabled = GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled;
  63. if(async)
  64. {
  65. task.reset(AsyncLoader::getSingleton().newTask<LoadTask>(this));
  66. ctx = &task->m_ctx;
  67. }
  68. else
  69. {
  70. task.reset(nullptr);
  71. ctx = &localCtx;
  72. }
  73. // Open file
  74. MeshBinaryLoader& loader = ctx->m_loader;
  75. ANKI_CHECK(loader.load(filename));
  76. const MeshBinaryHeader& header = loader.getHeader();
  77. // Misc
  78. m_indexType = header.m_indexType;
  79. m_aabb.setMin(header.m_boundingVolume.m_aabbMin);
  80. m_aabb.setMax(header.m_boundingVolume.m_aabbMax);
  81. m_positionsScale = header.m_vertexAttributes[VertexStreamId::kPosition].m_scale[0];
  82. m_positionsTranslation = Vec3(&header.m_vertexAttributes[VertexStreamId::kPosition].m_translation[0]);
  83. m_isConvex = !!(loader.getHeader().m_flags & MeshBinaryFlag::kConvex);
  84. // Submeshes
  85. m_subMeshes.resize(header.m_subMeshCount);
  86. for(U32 i = 0; i < m_subMeshes.getSize(); ++i)
  87. {
  88. for(U32 lod = 0; lod < header.m_lodCount; ++lod)
  89. {
  90. m_subMeshes[i].m_firstIndices[lod] = loader.getSubMeshes()[i].m_lods[lod].m_firstIndex;
  91. m_subMeshes[i].m_indexCounts[lod] = loader.getSubMeshes()[i].m_lods[lod].m_indexCount;
  92. m_subMeshes[i].m_firstMeshlet[lod] = loader.getSubMeshes()[i].m_lods[lod].m_firstMeshlet;
  93. m_subMeshes[i].m_meshletCounts[lod] = loader.getSubMeshes()[i].m_lods[lod].m_meshletCount;
  94. }
  95. m_subMeshes[i].m_aabb.setMin(loader.getSubMeshes()[i].m_boundingVolume.m_aabbMin);
  96. m_subMeshes[i].m_aabb.setMax(loader.getSubMeshes()[i].m_boundingVolume.m_aabbMax);
  97. }
  98. // LODs
  99. m_lods.resize(header.m_lodCount);
  100. for(I32 l = I32(header.m_lodCount - 1); l >= 0; --l)
  101. {
  102. Lod& lod = m_lods[l];
  103. // Index stuff
  104. lod.m_indexCount = header.m_indexCounts[l];
  105. ANKI_ASSERT((lod.m_indexCount % 3) == 0 && "Expecting triangles");
  106. const PtrSize indexBufferSize = PtrSize(lod.m_indexCount) * getIndexSize(m_indexType);
  107. lod.m_indexBufferAllocationToken = UnifiedGeometryBuffer::getSingleton().allocate(indexBufferSize, getIndexSize(m_indexType));
  108. // Vertex stuff
  109. lod.m_vertexCount = header.m_vertexCounts[l];
  110. for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
  111. {
  112. if(header.m_vertexAttributes[stream].m_format == Format::kNone)
  113. {
  114. continue;
  115. }
  116. m_presentVertStreams |= VertexStreamMask(1 << stream);
  117. lod.m_vertexBuffersAllocationToken[stream] =
  118. UnifiedGeometryBuffer::getSingleton().allocateFormat(kMeshRelatedVertexStreamFormats[stream], lod.m_vertexCount);
  119. }
  120. // Meshlet
  121. if(GrManager::getSingleton().getDeviceCapabilities().m_meshShaders || g_cvarCoreMeshletRendering)
  122. {
  123. const PtrSize meshletIndicesSize = header.m_meshletPrimitiveCounts[l] * sizeof(U8Vec4);
  124. lod.m_meshletIndices = UnifiedGeometryBuffer::getSingleton().allocate(meshletIndicesSize, sizeof(U8Vec4));
  125. const PtrSize meshletBoundingVolumesSize = header.m_meshletCounts[l] * sizeof(MeshletBoundingVolume);
  126. lod.m_meshletBoundingVolumes = UnifiedGeometryBuffer::getSingleton().allocate(meshletBoundingVolumesSize, sizeof(MeshletBoundingVolume));
  127. const PtrSize meshletGeomDescriptorsSize = header.m_meshletCounts[l] * sizeof(MeshletGeometryDescriptor);
  128. lod.m_meshletGeometryDescriptors =
  129. UnifiedGeometryBuffer::getSingleton().allocate(meshletGeomDescriptorsSize, sizeof(MeshletGeometryDescriptor));
  130. lod.m_meshletCount = header.m_meshletCounts[l];
  131. }
  132. }
  133. // BLAS
  134. if(rayTracingEnabled)
  135. {
  136. for(U32 submeshIdx = 0; submeshIdx < m_subMeshes.getSize(); ++submeshIdx)
  137. {
  138. SubMesh& subMesh = m_subMeshes[submeshIdx];
  139. for(U32 lodIdx = 0; lodIdx < header.m_lodCount; ++lodIdx)
  140. {
  141. Lod& lod = m_lods[lodIdx];
  142. AccelerationStructureInitInfo inf(ResourceString().sprintf("%s_%s", "BLAS", basename.cstr()));
  143. inf.m_type = AccelerationStructureType::kBottomLevel;
  144. inf.m_bottomLevel.m_indexBuffer = BufferView(lod.m_indexBufferAllocationToken)
  145. .incrementOffset(getIndexSize(m_indexType) * subMesh.m_firstIndices[lodIdx])
  146. .setRange(getIndexSize(m_indexType) * subMesh.m_indexCounts[lodIdx]);
  147. inf.m_bottomLevel.m_indexCount = subMesh.m_indexCounts[lodIdx];
  148. inf.m_bottomLevel.m_indexType = m_indexType;
  149. inf.m_bottomLevel.m_positionBuffer = lod.m_vertexBuffersAllocationToken[VertexStreamId::kPosition];
  150. inf.m_bottomLevel.m_positionStride = getFormatInfo(kMeshRelatedVertexStreamFormats[VertexStreamId::kPosition]).m_texelSize;
  151. inf.m_bottomLevel.m_positionsFormat = kMeshRelatedVertexStreamFormats[VertexStreamId::kPosition];
  152. inf.m_bottomLevel.m_positionCount = lod.m_vertexCount;
  153. const PtrSize requiredMemory = GrManager::getSingleton().getAccelerationStructureMemoryRequirement(inf);
  154. subMesh.m_blasAllocationTokens[lodIdx] = UnifiedGeometryBuffer::getSingleton().allocate(requiredMemory, 1);
  155. inf.m_accelerationStructureBuffer = subMesh.m_blasAllocationTokens[lodIdx];
  156. subMesh.m_blas[lodIdx] = GrManager::getSingleton().newAccelerationStructure(inf);
  157. }
  158. }
  159. }
  160. // Clear the buffers
  161. if(async)
  162. {
  163. CommandBufferInitInfo cmdbinit("MeshResourceClear");
  164. cmdbinit.m_flags = CommandBufferFlag::kSmallBatch | CommandBufferFlag::kGeneralWork;
  165. CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbinit);
  166. for(const Lod& lod : m_lods)
  167. {
  168. cmdb->zeroBuffer(lod.m_indexBufferAllocationToken.getCompleteBufferView());
  169. for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
  170. {
  171. if(header.m_vertexAttributes[stream].m_format != Format::kNone)
  172. {
  173. cmdb->zeroBuffer(lod.m_vertexBuffersAllocationToken[stream].getCompleteBufferView());
  174. }
  175. }
  176. if(lod.m_meshletIndices.isValid())
  177. {
  178. cmdb->zeroBuffer(lod.m_meshletIndices);
  179. cmdb->zeroBuffer(lod.m_meshletBoundingVolumes);
  180. cmdb->zeroBuffer(lod.m_meshletGeometryDescriptors);
  181. }
  182. }
  183. const BufferBarrierInfo barrier = {UnifiedGeometryBuffer::getSingleton().getBufferView(), BufferUsageBit::kCopyDestination,
  184. BufferUsageBit::kVertexOrIndex};
  185. cmdb->setPipelineBarrier({}, {&barrier, 1}, {});
  186. cmdb->endRecording();
  187. GrManager::getSingleton().submit(cmdb.get());
  188. }
  189. // Submit the loading task
  190. if(async)
  191. {
  192. AsyncLoader::getSingleton().submitTask(task.get());
  193. LoadTask* pTask;
  194. task.moveAndReset(pTask);
  195. }
  196. else
  197. {
  198. ANKI_CHECK(loadAsync(loader));
  199. }
  200. return Error::kNone;
  201. }
  202. Error MeshResource::loadAsync(MeshBinaryLoader& loader) const
  203. {
  204. GrManager& gr = GrManager::getSingleton();
  205. TransferGpuAllocator& transferAlloc = TransferGpuAllocator::getSingleton();
  206. Array<TransferGpuAllocatorHandle, kMaxLodCount*(U32(VertexStreamId::kMeshRelatedCount) + 1 + 3)> handles;
  207. U32 handleCount = 0;
  208. Buffer* unifiedGeometryBuffer = &UnifiedGeometryBuffer::getSingleton().getBuffer();
  209. const BufferUsageBit unifiedGeometryBufferNonTransferUsage = unifiedGeometryBuffer->getBufferUsage() ^ BufferUsageBit::kCopyDestination;
  210. CommandBufferInitInfo cmdbinit;
  211. cmdbinit.m_flags = CommandBufferFlag::kSmallBatch | CommandBufferFlag::kGeneralWork;
  212. CommandBufferPtr cmdb = gr.newCommandBuffer(cmdbinit);
  213. // Set transfer to transfer barrier because of the clear that happened while sync loading
  214. const BufferBarrierInfo barrier = {UnifiedGeometryBuffer::getSingleton().getBufferView(), unifiedGeometryBufferNonTransferUsage,
  215. BufferUsageBit::kCopyDestination};
  216. cmdb->setPipelineBarrier({}, {&barrier, 1}, {});
  217. // Upload index and vertex buffers
  218. for(U32 lodIdx = 0; lodIdx < m_lods.getSize(); ++lodIdx)
  219. {
  220. const Lod& lod = m_lods[lodIdx];
  221. // Upload index buffer
  222. {
  223. TransferGpuAllocatorHandle& handle = handles[handleCount++];
  224. ANKI_CHECK(transferAlloc.allocate(lod.m_indexBufferAllocationToken.getAllocatedSize(), handle));
  225. void* data = handle.getMappedMemory();
  226. ANKI_ASSERT(data);
  227. ANKI_CHECK(loader.storeIndexBuffer(lodIdx, data, handle.getRange()));
  228. cmdb->copyBufferToBuffer(handle, lod.m_indexBufferAllocationToken);
  229. }
  230. // Upload vert buffers
  231. for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
  232. {
  233. if(!(m_presentVertStreams & VertexStreamMask(1 << stream)))
  234. {
  235. continue;
  236. }
  237. TransferGpuAllocatorHandle& handle = handles[handleCount++];
  238. ANKI_CHECK(transferAlloc.allocate(lod.m_vertexBuffersAllocationToken[stream].getAllocatedSize(), handle));
  239. U8* data = static_cast<U8*>(handle.getMappedMemory());
  240. ANKI_ASSERT(data);
  241. // Load to staging
  242. ANKI_CHECK(loader.storeVertexBuffer(lodIdx, U32(stream), data, handle.getRange()));
  243. // Copy
  244. cmdb->copyBufferToBuffer(handle, lod.m_vertexBuffersAllocationToken[stream]);
  245. }
  246. if(lod.m_meshletBoundingVolumes.isValid())
  247. {
  248. // Indices
  249. TransferGpuAllocatorHandle& handle = handles[handleCount++];
  250. const PtrSize primitivesSize = lod.m_meshletIndices.getAllocatedSize();
  251. ANKI_CHECK(transferAlloc.allocate(primitivesSize, handle));
  252. ANKI_CHECK(loader.storeMeshletIndicesBuffer(lodIdx, handle.getMappedMemory(), primitivesSize));
  253. cmdb->copyBufferToBuffer(handle, lod.m_meshletIndices);
  254. // Meshlets
  255. ResourceDynamicArray<MeshBinaryMeshlet> binaryMeshlets;
  256. binaryMeshlets.resize(loader.getHeader().m_meshletCounts[lodIdx]);
  257. ANKI_CHECK(loader.storeMeshletBuffer(lodIdx, WeakArray(binaryMeshlets)));
  258. TransferGpuAllocatorHandle& handle2 = handles[handleCount++];
  259. ANKI_CHECK(transferAlloc.allocate(lod.m_meshletBoundingVolumes.getAllocatedSize(), handle2));
  260. WeakArray<MeshletBoundingVolume> outMeshletBoundingVolumes(static_cast<MeshletBoundingVolume*>(handle2.getMappedMemory()),
  261. loader.getHeader().m_meshletCounts[lodIdx]);
  262. TransferGpuAllocatorHandle& handle3 = handles[handleCount++];
  263. ANKI_CHECK(transferAlloc.allocate(lod.m_meshletGeometryDescriptors.getAllocatedSize(), handle3));
  264. WeakArray<MeshletGeometryDescriptor> outMeshletGeomDescriptors(static_cast<MeshletGeometryDescriptor*>(handle3.getMappedMemory()),
  265. loader.getHeader().m_meshletCounts[lodIdx]);
  266. for(U32 i = 0; i < binaryMeshlets.getSize(); ++i)
  267. {
  268. const MeshBinaryMeshlet& inMeshlet = binaryMeshlets[i];
  269. MeshletGeometryDescriptor& outMeshletGeom = outMeshletGeomDescriptors[i];
  270. MeshletBoundingVolume& outMeshletBoundingVolume = outMeshletBoundingVolumes[i];
  271. outMeshletBoundingVolume = {};
  272. outMeshletGeom = {};
  273. for(VertexStreamId stream : EnumIterable(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
  274. {
  275. if(!(m_presentVertStreams & VertexStreamMask(1u << stream)))
  276. {
  277. continue;
  278. }
  279. outMeshletGeom.m_vertexOffsets[U32(stream)] =
  280. lod.m_vertexBuffersAllocationToken[stream].getOffset() / getFormatInfo(kMeshRelatedVertexStreamFormats[stream]).m_texelSize
  281. + inMeshlet.m_firstVertex;
  282. }
  283. outMeshletGeom.m_firstPrimitive =
  284. lod.m_meshletIndices.getOffset() / getFormatInfo(kMeshletPrimitiveFormat).m_texelSize + inMeshlet.m_firstPrimitive;
  285. outMeshletGeom.m_primitiveCount_R16_Uint_vertexCount_R16_Uint = (inMeshlet.m_primitiveCount << 16u) | inMeshlet.m_vertexCount;
  286. outMeshletGeom.m_positionTranslation = m_positionsTranslation;
  287. outMeshletGeom.m_positionScale = m_positionsScale;
  288. outMeshletBoundingVolume.m_aabbMin = inMeshlet.m_boundingVolume.m_aabbMin;
  289. outMeshletBoundingVolume.m_aabbMax = inMeshlet.m_boundingVolume.m_aabbMax;
  290. outMeshletBoundingVolume.m_coneDirection_R8G8B8_Snorm_cosHalfAngle_R8_Snorm =
  291. packSnorm4x8(Vec4(inMeshlet.m_coneDirection, cos(inMeshlet.m_coneAngle / 2.0f)));
  292. outMeshletBoundingVolume.m_coneApex = inMeshlet.m_coneApex;
  293. outMeshletBoundingVolume.m_sphereRadius =
  294. ((outMeshletBoundingVolume.m_aabbMin + outMeshletBoundingVolume.m_aabbMax) / 2.0f - outMeshletBoundingVolume.m_aabbMax).length();
  295. outMeshletBoundingVolume.m_primitiveCount = inMeshlet.m_primitiveCount;
  296. }
  297. cmdb->copyBufferToBuffer(handle2, lod.m_meshletBoundingVolumes);
  298. cmdb->copyBufferToBuffer(handle3, lod.m_meshletGeometryDescriptors);
  299. }
  300. }
  301. if(gr.getDeviceCapabilities().m_rayTracingEnabled)
  302. {
  303. // Build BLASes
  304. for(U32 submeshIdx = 0; submeshIdx < m_subMeshes.getSize(); ++submeshIdx)
  305. {
  306. const SubMesh& submesh = m_subMeshes[submeshIdx];
  307. // Set the barriers
  308. BufferBarrierInfo bufferBarrier;
  309. bufferBarrier.m_bufferView = UnifiedGeometryBuffer::getSingleton().getBufferView();
  310. bufferBarrier.m_previousUsage = BufferUsageBit::kCopyDestination;
  311. bufferBarrier.m_nextUsage = unifiedGeometryBufferNonTransferUsage;
  312. Array<AccelerationStructureBarrierInfo, kMaxLodCount> asBarriers;
  313. for(U32 lodIdx = 0; lodIdx < m_lods.getSize(); ++lodIdx)
  314. {
  315. asBarriers[lodIdx].m_as = submesh.m_blas[lodIdx].get();
  316. asBarriers[lodIdx].m_previousUsage = AccelerationStructureUsageBit::kNone;
  317. asBarriers[lodIdx].m_nextUsage = AccelerationStructureUsageBit::kBuild;
  318. }
  319. cmdb->setPipelineBarrier({}, {&bufferBarrier, 1}, {&asBarriers[0], m_lods.getSize()});
  320. // Build BLASes
  321. for(U32 lodIdx = 0; lodIdx < m_lods.getSize(); ++lodIdx)
  322. {
  323. Bool addBarrier;
  324. const BufferView scratchBuff =
  325. AccelerationStructureScratchAllocator::getSingleton().allocate(submesh.m_blas[lodIdx]->getBuildScratchBufferSize(), addBarrier);
  326. if(addBarrier)
  327. {
  328. BufferBarrierInfo barr;
  329. barr.m_bufferView = scratchBuff;
  330. barr.m_previousUsage = BufferUsageBit::kAccelerationStructureBuildScratch;
  331. barr.m_nextUsage = BufferUsageBit::kAccelerationStructureBuildScratch;
  332. cmdb->setPipelineBarrier({}, {&barr, 1}, {});
  333. }
  334. cmdb->buildAccelerationStructure(submesh.m_blas[lodIdx].get(), scratchBuff);
  335. }
  336. // Barriers again
  337. for(U32 lodIdx = 0; lodIdx < m_lods.getSize(); ++lodIdx)
  338. {
  339. asBarriers[lodIdx].m_as = submesh.m_blas[lodIdx].get();
  340. asBarriers[lodIdx].m_previousUsage = AccelerationStructureUsageBit::kBuild;
  341. asBarriers[lodIdx].m_nextUsage = AccelerationStructureUsageBit::kAllRead;
  342. }
  343. cmdb->setPipelineBarrier({}, {}, {&asBarriers[0], m_lods.getSize()});
  344. }
  345. }
  346. else
  347. {
  348. // Only set a barrier
  349. BufferBarrierInfo bufferBarrier;
  350. bufferBarrier.m_bufferView = UnifiedGeometryBuffer::getSingleton().getBufferView();
  351. bufferBarrier.m_previousUsage = BufferUsageBit::kCopyDestination;
  352. bufferBarrier.m_nextUsage = unifiedGeometryBufferNonTransferUsage;
  353. cmdb->setPipelineBarrier({}, {&bufferBarrier, 1}, {});
  354. }
  355. // Finalize
  356. FencePtr fence;
  357. cmdb->endRecording();
  358. GrManager::getSingleton().submit(cmdb.get(), {}, &fence);
  359. for(U32 i = 0; i < handleCount; ++i)
  360. {
  361. transferAlloc.release(handles[i], fence);
  362. }
  363. return Error::kNone;
  364. }
  365. Error MeshResource::getOrCreateCollisionShape(Bool wantStatic, U32 lod, PhysicsCollisionShapePtr& out) const
  366. {
  367. lod = min<U32>(lod, getLodCount() - 1);
  368. Bool isConvex = m_isConvex;
  369. if(!isConvex && !wantStatic)
  370. {
  371. ANKI_RESOURCE_LOGW("Requesting a non-static non-convex collision shape is not supported. Will create a convex hull instead");
  372. isConvex = true;
  373. }
  374. const Lod& l = m_lods[lod];
  375. LockGuard lock(l.m_collisionShapeMtx);
  376. if(!l.m_collisionShapes[isConvex])
  377. {
  378. MeshBinaryLoader loader(&ResourceMemoryPool::getSingleton());
  379. ANKI_CHECK(loader.load(getFilename()));
  380. ResourceDynamicArray<Vec3> positions;
  381. ResourceDynamicArray<U32> indices;
  382. ANKI_CHECK(loader.storeIndicesAndPosition(lod, indices, positions));
  383. if(isConvex)
  384. {
  385. l.m_collisionShapes[isConvex] = PhysicsWorld::getSingleton().newConvexHullShape(positions);
  386. }
  387. else
  388. {
  389. l.m_collisionShapes[isConvex] = PhysicsWorld::getSingleton().newStaticMeshShape(positions, indices);
  390. }
  391. }
  392. out = l.m_collisionShapes[isConvex];
  393. return Error::kNone;
  394. }
  395. } // end namespace anki